xfs_dir2_sf.c 33.1 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"
20
#include "xfs_format.h"
21 22
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
L
Linus Torvalds 已提交
23
#include "xfs_mount.h"
24
#include "xfs_da_format.h"
25
#include "xfs_da_btree.h"
L
Linus Torvalds 已提交
26
#include "xfs_inode.h"
27
#include "xfs_trans.h"
28
#include "xfs_inode_item.h"
L
Linus Torvalds 已提交
29
#include "xfs_error.h"
30
#include "xfs_dir2.h"
C
Christoph Hellwig 已提交
31
#include "xfs_dir2_priv.h"
C
Christoph Hellwig 已提交
32
#include "xfs_trace.h"
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

/*
 * 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 已提交
51

L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59 60 61 62 63
static void xfs_dir2_sf_toino4(xfs_da_args_t *args);
static void xfs_dir2_sf_toino8(xfs_da_args_t *args);

/*
 * 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 */
64
	xfs_dir2_data_hdr_t	*hdr,		/* block directory data */
L
Linus Torvalds 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77
	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 */
78
	xfs_ino_t		parent = 0;	/* parent inode number */
L
Linus Torvalds 已提交
79
	int			size=0;		/* total computed size */
80
	int			has_ftype;
81
	struct xfs_da_geometry	*geo;
L
Linus Torvalds 已提交
82 83

	mp = dp->i_mount;
84
	geo = mp->m_dir_geo;
L
Linus Torvalds 已提交
85

86 87 88 89 90 91
	/*
	 * 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 已提交
92
	count = i8count = namelen = 0;
93
	btp = xfs_dir2_block_tail_p(geo, hdr);
94
	blp = xfs_dir2_block_leaf_p(btp);
L
Linus Torvalds 已提交
95 96 97 98

	/*
	 * Iterate over the block's data entries by using the leaf pointers.
	 */
99
	for (i = 0; i < be32_to_cpu(btp->count); i++) {
100
		if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR)
L
Linus Torvalds 已提交
101 102 103 104
			continue;
		/*
		 * Calculate the pointer to the entry at hand.
		 */
105
		dep = (xfs_dir2_data_entry_t *)((char *)hdr +
106
				xfs_dir2_dataptr_to_off(geo, addr));
L
Linus Torvalds 已提交
107 108 109 110 111 112 113 114 115
		/*
		 * 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 已提交
116

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

120
		/* take into account the file type field */
L
Linus Torvalds 已提交
121 122
		if (!isdot && !isdotdot) {
			count++;
123
			namelen += dep->namelen + has_ftype;
L
Linus Torvalds 已提交
124
		} else if (isdotdot)
125
			parent = be64_to_cpu(dep->inumber);
L
Linus Torvalds 已提交
126 127 128
		/*
		 * Calculate the new size, see if we should give up yet.
		 */
C
Christoph Hellwig 已提交
129 130 131 132
		size = xfs_dir2_sf_hdr_size(i8count) +	/* header */
		       count * 3 * sizeof(u8) +		/* namelen + offset */
		       namelen +			/* name */
		       (i8count ?			/* inumber */
C
Christoph Hellwig 已提交
133 134
				count * XFS_INO64_SIZE :
				count * XFS_INO32_SIZE);
L
Linus Torvalds 已提交
135 136 137 138 139 140 141 142
		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;
143
	dp->d_ops->sf_put_parent_ino(sfhp, parent);
L
Linus Torvalds 已提交
144 145 146 147 148 149 150 151 152 153
	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 */
154
	struct xfs_buf		*bp,
L
Linus Torvalds 已提交
155 156 157
	int			size,		/* shortform directory size */
	xfs_dir2_sf_hdr_t	*sfhp)		/* shortform directory hdr */
{
158
	xfs_dir2_data_hdr_t	*hdr;		/* block header */
L
Linus Torvalds 已提交
159 160 161 162 163 164 165 166 167 168
	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 已提交
169
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform directory header */
170
	xfs_dir2_sf_hdr_t	*dst;		/* temporary data buffer */
L
Linus Torvalds 已提交
171

C
Christoph Hellwig 已提交
172 173
	trace_xfs_dir2_block_to_sf(args);

L
Linus Torvalds 已提交
174 175 176 177
	dp = args->dp;
	mp = dp->i_mount;

	/*
178 179 180 181
	 * 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 已提交
182
	 */
183 184
	dst = kmem_alloc(mp->m_sb.sb_inodesize, KM_SLEEP);
	hdr = bp->b_addr;
185

L
Linus Torvalds 已提交
186 187 188
	/*
	 * Copy the header into the newly allocate local space.
	 */
189
	sfp = (xfs_dir2_sf_hdr_t *)dst;
190
	memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count));
191

L
Linus Torvalds 已提交
192 193 194
	/*
	 * Set up to loop over the block's entries.
	 */
195
	btp = xfs_dir2_block_tail_p(args->geo, hdr);
196
	ptr = (char *)dp->d_ops->data_entry_p(hdr);
197 198
	endptr = (char *)xfs_dir2_block_leaf_p(btp);
	sfep = xfs_dir2_sf_firstentry(sfp);
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206 207
	/*
	 * 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;
208 209
		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
			ptr += be16_to_cpu(dup->length);
L
Linus Torvalds 已提交
210 211 212 213 214 215 216
			continue;
		}
		dep = (xfs_dir2_data_entry_t *)ptr;
		/*
		 * Skip .
		 */
		if (dep->namelen == 1 && dep->name[0] == '.')
217
			ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino);
L
Linus Torvalds 已提交
218 219 220 221 222
		/*
		 * Skip .., but make sure the inode number is right.
		 */
		else if (dep->namelen == 2 &&
			 dep->name[0] == '.' && dep->name[1] == '.')
223
			ASSERT(be64_to_cpu(dep->inumber) ==
224
			       dp->d_ops->sf_get_parent_ino(sfp));
L
Linus Torvalds 已提交
225 226 227 228 229
		/*
		 * Normal entry, copy it into shortform.
		 */
		else {
			sfep->namelen = dep->namelen;
230
			xfs_dir2_sf_put_offset(sfep,
L
Linus Torvalds 已提交
231
				(xfs_dir2_data_aoff_t)
232
				((char *)dep - (char *)hdr));
L
Linus Torvalds 已提交
233
			memcpy(sfep->name, dep->name, dep->namelen);
234 235 236
			dp->d_ops->sf_put_ino(sfp, sfep,
					      be64_to_cpu(dep->inumber));
			dp->d_ops->sf_put_ftype(sfep,
237
					dp->d_ops->data_get_ftype(dep));
238

239
			sfep = dp->d_ops->sf_nextentry(sfp, sfep);
L
Linus Torvalds 已提交
240
		}
241
		ptr += dp->d_ops->data_entsize(dep->namelen);
L
Linus Torvalds 已提交
242 243
	}
	ASSERT((char *)sfep - (char *)sfp == size);
244 245 246

	/* now we are done with the block, we can shrink the inode */
	logflags = XFS_ILOG_CORE;
247
	error = xfs_dir2_shrink_inode(args, args->geo->datablk, bp);
248
	if (error) {
D
Dave Chinner 已提交
249
		ASSERT(error != -ENOSPC);
250 251 252 253 254 255 256 257 258 259
		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);
260 261 262
	xfs_init_local_fork(dp, XFS_DATA_FORK, dst, size);
	dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
	dp->i_d.di_size = size;
263 264

	logflags |= XFS_ILOG_DDATA;
L
Linus Torvalds 已提交
265 266 267
	xfs_dir2_sf_check(args);
out:
	xfs_trans_log_inode(args->trans, dp, logflags);
268
	kmem_free(dst);
L
Linus Torvalds 已提交
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
	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 */
	int			new_isize;	/* di_size after adding name */
	int			objchange;	/* changing to 8-byte inodes */
287
	xfs_dir2_data_aoff_t	offset = 0;	/* offset for new entry */
L
Linus Torvalds 已提交
288
	int			pick;		/* which algorithm to use */
C
Christoph Hellwig 已提交
289
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
290
	xfs_dir2_sf_entry_t	*sfep = NULL;	/* shortform entry */
L
Linus Torvalds 已提交
291

C
Christoph Hellwig 已提交
292 293
	trace_xfs_dir2_sf_addname(args);

D
Dave Chinner 已提交
294
	ASSERT(xfs_dir2_sf_lookup(args) == -ENOENT);
L
Linus Torvalds 已提交
295 296 297 298 299 300 301
	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));
D
Dave Chinner 已提交
302
		return -EIO;
L
Linus Torvalds 已提交
303 304 305
	}
	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
306 307
	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 已提交
308 309 310
	/*
	 * Compute entry (and change in) size.
	 */
311
	incr_isize = dp->d_ops->sf_entsize(sfp, args->namelen);
L
Linus Torvalds 已提交
312
	objchange = 0;
C
Christoph Hellwig 已提交
313

L
Linus Torvalds 已提交
314 315 316
	/*
	 * Do we have to change to 8 byte inodes?
	 */
C
Christoph Hellwig 已提交
317
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
318
		/*
319
		 * Yes, adjust the inode size.  old count + (parent + new)
L
Linus Torvalds 已提交
320
		 */
C
Christoph Hellwig 已提交
321
		incr_isize += (sfp->count + 2) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
322 323
		objchange = 1;
	}
C
Christoph Hellwig 已提交
324

325
	new_isize = (int)dp->i_d.di_size + incr_isize;
L
Linus Torvalds 已提交
326 327 328 329 330 331 332 333 334 335
	/*
	 * 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.
		 */
336
		if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
D
Dave Chinner 已提交
337
			return -ENOSPC;
L
Linus Torvalds 已提交
338 339 340 341 342 343 344 345 346 347 348
		/*
		 * 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.
	 */
349
	if (args->op_flags & XFS_DA_OP_JUSTCHECK)
L
Linus Torvalds 已提交
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
		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 */
{
	int			byteoff;	/* byte offset in sf dir */
	xfs_inode_t		*dp;		/* incore directory inode */
C
Christoph Hellwig 已提交
386
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
387 388 389

	dp = args->dp;

C
Christoph Hellwig 已提交
390
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
391 392 393 394
	byteoff = (int)((char *)sfep - (char *)sfp);
	/*
	 * Grow the in-inode space.
	 */
395
	xfs_idata_realloc(dp, dp->d_ops->sf_entsize(sfp, args->namelen),
396
			  XFS_DATA_FORK);
L
Linus Torvalds 已提交
397 398 399
	/*
	 * Need to set up again due to realloc of the inode data.
	 */
C
Christoph Hellwig 已提交
400
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
401 402 403 404 405
	sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff);
	/*
	 * Fill in the new entry.
	 */
	sfep->namelen = args->namelen;
406
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
407
	memcpy(sfep->name, args->name, sfep->namelen);
408 409
	dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
	dp->d_ops->sf_put_ftype(sfep, args->filetype);
410

L
Linus Torvalds 已提交
411 412 413
	/*
	 * Update the header and inode.
	 */
C
Christoph Hellwig 已提交
414
	sfp->count++;
L
Linus Torvalds 已提交
415
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM)
C
Christoph Hellwig 已提交
416
		sfp->i8count++;
L
Linus Torvalds 已提交
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
	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 已提交
445
	xfs_dir2_sf_hdr_t	*oldsfp;	/* original shortform dir */
L
Linus Torvalds 已提交
446
	xfs_dir2_sf_entry_t	*sfep;		/* entry in new dir */
C
Christoph Hellwig 已提交
447
	xfs_dir2_sf_hdr_t	*sfp;		/* new shortform dir */
L
Linus Torvalds 已提交
448 449 450 451 452 453

	/*
	 * Copy the old directory to the stack buffer.
	 */
	dp = args->dp;

C
Christoph Hellwig 已提交
454
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
455 456
	old_isize = (int)dp->i_d.di_size;
	buf = kmem_alloc(old_isize, KM_SLEEP);
C
Christoph Hellwig 已提交
457
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
L
Linus Torvalds 已提交
458 459 460 461 462 463
	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.
	 */
464
	for (offset = dp->d_ops->data_first_offset,
465
	      oldsfep = xfs_dir2_sf_firstentry(oldsfp),
466
	      add_datasize = dp->d_ops->data_entsize(args->namelen),
L
Linus Torvalds 已提交
467 468
	      eof = (char *)oldsfep == &buf[old_isize];
	     !eof;
469
	     offset = new_offset + dp->d_ops->data_entsize(oldsfep->namelen),
470
	      oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep),
L
Linus Torvalds 已提交
471
	      eof = (char *)oldsfep == &buf[old_isize]) {
472
		new_offset = xfs_dir2_sf_get_offset(oldsfep);
L
Linus Torvalds 已提交
473 474 475 476 477 478 479 480 481 482 483 484 485
		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 已提交
486
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
487 488 489 490 491 492 493 494 495 496
	/*
	 * 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;
497
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
498
	memcpy(sfep->name, args->name, sfep->namelen);
499 500
	dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
	dp->d_ops->sf_put_ftype(sfep, args->filetype);
C
Christoph Hellwig 已提交
501
	sfp->count++;
L
Linus Torvalds 已提交
502
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
C
Christoph Hellwig 已提交
503
		sfp->i8count++;
L
Linus Torvalds 已提交
504 505 506 507
	/*
	 * If there's more left to copy, do that.
	 */
	if (!eof) {
508
		sfep = dp->d_ops->sf_nextentry(sfp, sfep);
L
Linus Torvalds 已提交
509 510
		memcpy(sfep, oldsfep, old_isize - nbytes);
	}
511
	kmem_free(buf);
L
Linus Torvalds 已提交
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
	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_dir2_data_aoff_t	offset;		/* data block offset */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform entry */
C
Christoph Hellwig 已提交
535
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
536 537 538 539 540
	int			size;		/* entry's data size */
	int			used;		/* data bytes used */

	dp = args->dp;

C
Christoph Hellwig 已提交
541
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
542
	size = dp->d_ops->data_entsize(args->namelen);
543
	offset = dp->d_ops->data_first_offset;
544
	sfep = xfs_dir2_sf_firstentry(sfp);
L
Linus Torvalds 已提交
545 546 547 548 549 550
	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 已提交
551
	for (i = 0; i < sfp->count; i++) {
L
Linus Torvalds 已提交
552
		if (!holefit)
553 554
			holefit = offset + size <= xfs_dir2_sf_get_offset(sfep);
		offset = xfs_dir2_sf_get_offset(sfep) +
555
			 dp->d_ops->data_entsize(sfep->namelen);
556
		sfep = dp->d_ops->sf_nextentry(sfp, sfep);
L
Linus Torvalds 已提交
557 558 559 560 561 562
	}
	/*
	 * Calculate data bytes used excluding the new entry, if this
	 * was a data block (block form directory).
	 */
	used = offset +
C
Christoph Hellwig 已提交
563
	       (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
L
Linus Torvalds 已提交
564 565 566 567 568 569
	       (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.
	 */
570
	if (used + (holefit ? 0 : size) > args->geo->blksize)
L
Linus Torvalds 已提交
571 572 573 574
		return 0;
	/*
	 * If changing the inode number size, do it the hard way.
	 */
C
Christoph Hellwig 已提交
575
	if (objchange)
L
Linus Torvalds 已提交
576 577 578 579
		return 2;
	/*
	 * If it won't fit at the end then do it the hard way (use the hole).
	 */
580
	if (used + size > args->geo->blksize)
L
Linus Torvalds 已提交
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
		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 已提交
604
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
605 606 607

	dp = args->dp;

C
Christoph Hellwig 已提交
608
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
609
	offset = dp->d_ops->data_first_offset;
610
	ino = dp->d_ops->sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
611 612
	i8count = ino > XFS_DIR2_MAX_SHORT_INUM;

613
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
C
Christoph Hellwig 已提交
614
	     i < sfp->count;
615
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
616
		ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset);
617
		ino = dp->d_ops->sf_get_ino(sfp, sfep);
L
Linus Torvalds 已提交
618 619
		i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
		offset =
620
			xfs_dir2_sf_get_offset(sfep) +
621
			dp->d_ops->data_entsize(sfep->namelen);
622
		ASSERT(dp->d_ops->sf_get_ftype(sfep) < XFS_DIR3_FT_MAX);
L
Linus Torvalds 已提交
623
	}
C
Christoph Hellwig 已提交
624
	ASSERT(i8count == sfp->i8count);
L
Linus Torvalds 已提交
625 626
	ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size);
	ASSERT(offset +
C
Christoph Hellwig 已提交
627
	       (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
628
	       (uint)sizeof(xfs_dir2_block_tail_t) <= args->geo->blksize);
L
Linus Torvalds 已提交
629 630 631 632 633 634 635 636 637 638 639 640 641
}
#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 已提交
642
	xfs_dir2_sf_hdr_t *sfp;		/* shortform structure */
L
Linus Torvalds 已提交
643 644
	int		size;		/* directory size */

C
Christoph Hellwig 已提交
645 646
	trace_xfs_dir2_sf_create(args);

L
Linus Torvalds 已提交
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
	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;
664
	size = xfs_dir2_sf_hdr_size(i8count);
L
Linus Torvalds 已提交
665 666 667 668 669 670 671
	/*
	 * Make a buffer for the data.
	 */
	xfs_idata_realloc(dp, size, XFS_DATA_FORK);
	/*
	 * Fill in the header,
	 */
C
Christoph Hellwig 已提交
672 673
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	sfp->i8count = i8count;
L
Linus Torvalds 已提交
674 675 676
	/*
	 * Now can put in the inode number, since i8count is set.
	 */
677
	dp->d_ops->sf_put_parent_ino(sfp, pino);
C
Christoph Hellwig 已提交
678
	sfp->count = 0;
L
Linus Torvalds 已提交
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
	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 */
695
	int			error;
L
Linus Torvalds 已提交
696
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
697
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
698
	enum xfs_dacmp		cmp;		/* comparison result */
699
	xfs_dir2_sf_entry_t	*ci_sfep;	/* case-insens. entry */
L
Linus Torvalds 已提交
700

C
Christoph Hellwig 已提交
701 702
	trace_xfs_dir2_sf_lookup(args);

L
Linus Torvalds 已提交
703 704 705 706 707 708 709 710 711
	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));
D
Dave Chinner 已提交
712
		return -EIO;
L
Linus Torvalds 已提交
713 714 715
	}
	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
716 717
	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 已提交
718 719 720 721 722
	/*
	 * Special case for .
	 */
	if (args->namelen == 1 && args->name[0] == '.') {
		args->inumber = dp->i_ino;
723
		args->cmpresult = XFS_CMP_EXACT;
724
		args->filetype = XFS_DIR3_FT_DIR;
D
Dave Chinner 已提交
725
		return -EEXIST;
L
Linus Torvalds 已提交
726 727 728 729 730 731
	}
	/*
	 * Special case for ..
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
732
		args->inumber = dp->d_ops->sf_get_parent_ino(sfp);
733
		args->cmpresult = XFS_CMP_EXACT;
734
		args->filetype = XFS_DIR3_FT_DIR;
D
Dave Chinner 已提交
735
		return -EEXIST;
L
Linus Torvalds 已提交
736 737 738 739
	}
	/*
	 * Loop over all the entries trying to match ours.
	 */
740
	ci_sfep = NULL;
C
Christoph Hellwig 已提交
741
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
742
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
743 744 745 746 747 748 749 750 751
		/*
		 * 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;
752 753
			args->inumber = dp->d_ops->sf_get_ino(sfp, sfep);
			args->filetype = dp->d_ops->sf_get_ftype(sfep);
754
			if (cmp == XFS_CMP_EXACT)
D
Dave Chinner 已提交
755
				return -EEXIST;
756
			ci_sfep = sfep;
L
Linus Torvalds 已提交
757 758
		}
	}
759
	ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
760 761
	/*
	 * Here, we can only be doing a lookup (not a rename or replace).
D
Dave Chinner 已提交
762
	 * If a case-insensitive match was not found, return -ENOENT.
763
	 */
764
	if (!ci_sfep)
D
Dave Chinner 已提交
765
		return -ENOENT;
766 767
	/* 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 已提交
768
	return error;
L
Linus Torvalds 已提交
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
}

/*
 * 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 已提交
785
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
786

C
Christoph Hellwig 已提交
787 788
	trace_xfs_dir2_sf_removename(args);

L
Linus Torvalds 已提交
789 790 791 792 793 794 795 796 797
	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));
D
Dave Chinner 已提交
798
		return -EIO;
L
Linus Torvalds 已提交
799 800 801
	}
	ASSERT(dp->i_df.if_bytes == oldsize);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
802 803
	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 已提交
804 805 806 807
	/*
	 * Loop over the old directory entries.
	 * Find the one we're deleting.
	 */
C
Christoph Hellwig 已提交
808
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
809
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
810 811
		if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
812
			ASSERT(dp->d_ops->sf_get_ino(sfp, sfep) ==
813
			       args->inumber);
L
Linus Torvalds 已提交
814 815 816 817 818 819
			break;
		}
	}
	/*
	 * Didn't find it.
	 */
C
Christoph Hellwig 已提交
820
	if (i == sfp->count)
D
Dave Chinner 已提交
821
		return -ENOENT;
L
Linus Torvalds 已提交
822 823 824 825
	/*
	 * Calculate sizes.
	 */
	byteoff = (int)((char *)sfep - (char *)sfp);
826
	entsize = dp->d_ops->sf_entsize(sfp, args->namelen);
L
Linus Torvalds 已提交
827 828 829 830 831 832 833 834 835 836
	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 已提交
837
	sfp->count--;
L
Linus Torvalds 已提交
838 839 840 841 842
	dp->i_d.di_size = newsize;
	/*
	 * Reallocate, making it smaller.
	 */
	xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK);
C
Christoph Hellwig 已提交
843
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
844 845 846 847
	/*
	 * Are we changing inode number size?
	 */
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
C
Christoph Hellwig 已提交
848
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
849 850
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
851
			sfp->i8count--;
L
Linus Torvalds 已提交
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
	}
	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 */
	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 已提交
870
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
871

C
Christoph Hellwig 已提交
872 873
	trace_xfs_dir2_sf_replace(args);

L
Linus Torvalds 已提交
874 875 876 877 878 879 880 881
	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));
D
Dave Chinner 已提交
882
		return -EIO;
L
Linus Torvalds 已提交
883 884 885
	}
	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
886 887
	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));
C
Christoph Hellwig 已提交
888

L
Linus Torvalds 已提交
889 890 891
	/*
	 * New inode number is large, and need to convert to 8-byte inodes.
	 */
C
Christoph Hellwig 已提交
892
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
893 894 895
		int	error;			/* error return value */
		int	newsize;		/* new inode size */

C
Christoph Hellwig 已提交
896
		newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
		/*
		 * 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 已提交
912
		sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
913 914
	} else
		i8elevated = 0;
C
Christoph Hellwig 已提交
915

L
Linus Torvalds 已提交
916 917 918 919 920 921
	ASSERT(args->namelen != 1 || args->name[0] != '.');
	/*
	 * Replace ..'s entry.
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
922
		ino = dp->d_ops->sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
923
		ASSERT(args->inumber != ino);
924
		dp->d_ops->sf_put_parent_ino(sfp, args->inumber);
L
Linus Torvalds 已提交
925 926 927 928 929
	}
	/*
	 * Normal entry, look for the name.
	 */
	else {
930
		for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
931
		     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
932 933
			if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
934
				ino = dp->d_ops->sf_get_ino(sfp, sfep);
L
Linus Torvalds 已提交
935
				ASSERT(args->inumber != ino);
936 937
				dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
				dp->d_ops->sf_put_ftype(sfep, args->filetype);
L
Linus Torvalds 已提交
938 939 940 941 942 943
				break;
			}
		}
		/*
		 * Didn't find it.
		 */
C
Christoph Hellwig 已提交
944
		if (i == sfp->count) {
945
			ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
L
Linus Torvalds 已提交
946 947
			if (i8elevated)
				xfs_dir2_sf_toino4(args);
D
Dave Chinner 已提交
948
			return -ENOENT;
L
Linus Torvalds 已提交
949 950 951 952 953 954 955 956 957 958
		}
	}
	/*
	 * 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 已提交
959
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
960 961
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
962
			sfp->i8count--;
L
Linus Torvalds 已提交
963 964 965 966 967 968 969 970 971 972
	}
	/*
	 * 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 已提交
973
		ASSERT(sfp->i8count != 0);
L
Linus Torvalds 已提交
974
		if (!i8elevated)
C
Christoph Hellwig 已提交
975
			sfp->i8count++;
L
Linus Torvalds 已提交
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
	}
	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 */
{
	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 已提交
995
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
996 997
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
998
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
L
Linus Torvalds 已提交
999

C
Christoph Hellwig 已提交
1000 1001
	trace_xfs_dir2_sf_toino4(args);

L
Linus Torvalds 已提交
1002 1003 1004 1005 1006 1007 1008 1009 1010
	dp = args->dp;

	/*
	 * 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 已提交
1011 1012
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 1);
L
Linus Torvalds 已提交
1013 1014 1015 1016
	memcpy(buf, oldsfp, oldsize);
	/*
	 * Compute the new inode size.
	 */
C
Christoph Hellwig 已提交
1017
	newsize = oldsize - (oldsfp->count + 1) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
1018 1019 1020 1021 1022
	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 已提交
1023 1024
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1025 1026 1027
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1028 1029
	sfp->count = oldsfp->count;
	sfp->i8count = 0;
1030
	dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1031 1032 1033
	/*
	 * Copy the entries field by field.
	 */
1034 1035
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1036
	     i < sfp->count;
1037 1038
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep),
		  oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1039
		sfep->namelen = oldsfep->namelen;
C
Christoph Hellwig 已提交
1040
		memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset));
L
Linus Torvalds 已提交
1041
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1042 1043 1044
		dp->d_ops->sf_put_ino(sfp, sfep,
				      dp->d_ops->sf_get_ino(oldsfp, oldsfep));
		dp->d_ops->sf_put_ftype(sfep, dp->d_ops->sf_get_ftype(oldsfep));
L
Linus Torvalds 已提交
1045 1046 1047 1048
	}
	/*
	 * Clean up the inode.
	 */
1049
	kmem_free(buf);
L
Linus Torvalds 已提交
1050 1051 1052 1053 1054
	dp->i_d.di_size = newsize;
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}

/*
1055 1056 1057
 * 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 已提交
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
 */
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 已提交
1068
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1069 1070
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1071
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
L
Linus Torvalds 已提交
1072

C
Christoph Hellwig 已提交
1073 1074
	trace_xfs_dir2_sf_toino8(args);

L
Linus Torvalds 已提交
1075 1076 1077 1078 1079 1080 1081 1082 1083
	dp = args->dp;

	/*
	 * 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 已提交
1084 1085
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 0);
L
Linus Torvalds 已提交
1086 1087
	memcpy(buf, oldsfp, oldsize);
	/*
1088
	 * Compute the new inode size (nb: entry count + 1 for parent)
L
Linus Torvalds 已提交
1089
	 */
C
Christoph Hellwig 已提交
1090
	newsize = oldsize + (oldsfp->count + 1) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
1091 1092 1093 1094 1095
	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 已提交
1096 1097
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1098 1099 1100
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1101 1102
	sfp->count = oldsfp->count;
	sfp->i8count = 1;
1103
	dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1104 1105 1106
	/*
	 * Copy the entries field by field.
	 */
1107 1108
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1109
	     i < sfp->count;
1110 1111
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep),
		  oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1112
		sfep->namelen = oldsfep->namelen;
C
Christoph Hellwig 已提交
1113
		memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset));
L
Linus Torvalds 已提交
1114
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1115 1116 1117
		dp->d_ops->sf_put_ino(sfp, sfep,
				      dp->d_ops->sf_get_ino(oldsfp, oldsfep));
		dp->d_ops->sf_put_ftype(sfep, dp->d_ops->sf_get_ftype(oldsfep));
L
Linus Torvalds 已提交
1118 1119 1120 1121
	}
	/*
	 * Clean up the inode.
	 */
1122
	kmem_free(buf);
L
Linus Torvalds 已提交
1123 1124 1125
	dp->i_d.di_size = newsize;
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}