xfs_dir2_sf.c 34.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"
20
#include "xfs_format.h"
21 22
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
L
Linus Torvalds 已提交
23
#include "xfs_sb.h"
24
#include "xfs_ag.h"
L
Linus Torvalds 已提交
25
#include "xfs_mount.h"
26
#include "xfs_da_format.h"
27
#include "xfs_da_btree.h"
L
Linus Torvalds 已提交
28
#include "xfs_inode.h"
29
#include "xfs_trans.h"
30
#include "xfs_inode_item.h"
L
Linus Torvalds 已提交
31
#include "xfs_error.h"
32
#include "xfs_dir2.h"
C
Christoph Hellwig 已提交
33
#include "xfs_dir2_priv.h"
C
Christoph Hellwig 已提交
34
#include "xfs_trace.h"
35
#include "xfs_dinode.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 59 60 61 62 63 64 65 66 67

/*
 * 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 */

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

	mp = dp->i_mount;

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

	/*
	 * Iterate over the block's data entries by using the leaf pointers.
	 */
101
	for (i = 0; i < be32_to_cpu(btp->count); i++) {
102
		if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR)
L
Linus Torvalds 已提交
103 104 105 106 107
			continue;
		/*
		 * Calculate the pointer to the entry at hand.
		 */
		dep = (xfs_dir2_data_entry_t *)
108
		      ((char *)hdr + xfs_dir2_dataptr_to_off(mp, addr));
L
Linus Torvalds 已提交
109 110 111 112 113 114 115 116 117 118 119
		/*
		 * 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)
120
			i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM;
L
Linus Torvalds 已提交
121
#endif
122
		/* take into account the file type field */
L
Linus Torvalds 已提交
123 124
		if (!isdot && !isdotdot) {
			count++;
125
			namelen += dep->namelen + has_ftype;
L
Linus Torvalds 已提交
126
		} else if (isdotdot)
127
			parent = be64_to_cpu(dep->inumber);
L
Linus Torvalds 已提交
128 129 130
		/*
		 * Calculate the new size, see if we should give up yet.
		 */
131
		size = xfs_dir2_sf_hdr_size(i8count) +		/* header */
L
Linus Torvalds 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145
		       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;
146
	dp->d_ops->sf_put_parent_ino(sfhp, parent);
L
Linus Torvalds 已提交
147 148 149 150 151 152 153 154 155 156
	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 */
157
	struct xfs_buf		*bp,
L
Linus Torvalds 已提交
158 159 160
	int			size,		/* shortform directory size */
	xfs_dir2_sf_hdr_t	*sfhp)		/* shortform directory hdr */
{
161
	xfs_dir2_data_hdr_t	*hdr;		/* block header */
L
Linus Torvalds 已提交
162 163 164 165 166 167 168 169 170 171
	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 已提交
172
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform directory header */
173
	xfs_dir2_sf_hdr_t	*dst;		/* temporary data buffer */
L
Linus Torvalds 已提交
174

C
Christoph Hellwig 已提交
175 176
	trace_xfs_dir2_block_to_sf(args);

L
Linus Torvalds 已提交
177 178 179 180
	dp = args->dp;
	mp = dp->i_mount;

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

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

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

242
			sfep = dp->d_ops->sf_nextentry(sfp, sfep);
L
Linus Torvalds 已提交
243
		}
244
		ptr += dp->d_ops->data_entsize(dep->namelen);
L
Linus Torvalds 已提交
245 246
	}
	ASSERT((char *)sfep - (char *)sfp == size);
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

	/* now we are done with the block, we can shrink the inode */
	logflags = XFS_ILOG_CORE;
	error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp);
	if (error) {
		ASSERT(error != ENOSPC);
		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.
	 */
	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;
	memcpy(dp->i_df.if_u1.if_data, dst, size);
	dp->i_d.di_size = size;
L
Linus Torvalds 已提交
271 272 273
	xfs_dir2_sf_check(args);
out:
	xfs_trans_log_inode(args->trans, dp, logflags);
274
	kmem_free(dst);
L
Linus Torvalds 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
	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 */
293
	xfs_dir2_data_aoff_t	offset = 0;	/* offset for new entry */
L
Linus Torvalds 已提交
294
	int			pick;		/* which algorithm to use */
C
Christoph Hellwig 已提交
295
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
296
	xfs_dir2_sf_entry_t	*sfep = NULL;	/* shortform entry */
L
Linus Torvalds 已提交
297

C
Christoph Hellwig 已提交
298 299
	trace_xfs_dir2_sf_addname(args);

L
Linus Torvalds 已提交
300 301 302 303 304 305 306 307 308 309 310 311
	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 已提交
312 313
	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 已提交
314 315 316
	/*
	 * Compute entry (and change in) size.
	 */
317
	incr_isize = dp->d_ops->sf_entsize(sfp, args->namelen);
L
Linus Torvalds 已提交
318 319 320 321 322
	objchange = 0;
#if XFS_BIG_INUMS
	/*
	 * Do we have to change to 8 byte inodes?
	 */
C
Christoph Hellwig 已提交
323
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
324
		/*
325
		 * Yes, adjust the inode size.  old count + (parent + new)
L
Linus Torvalds 已提交
326 327
		 */
		incr_isize +=
C
Christoph Hellwig 已提交
328
			(sfp->count + 2) *
L
Linus Torvalds 已提交
329 330 331 332 333
			((uint)sizeof(xfs_dir2_ino8_t) -
			 (uint)sizeof(xfs_dir2_ino4_t));
		objchange = 1;
	}
#endif
334
	new_isize = (int)dp->i_d.di_size + incr_isize;
L
Linus Torvalds 已提交
335 336 337 338 339 340 341 342 343 344
	/*
	 * 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.
		 */
345
		if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
L
Linus Torvalds 已提交
346 347 348 349 350 351 352 353 354 355 356 357
			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.
	 */
358
	if (args->op_flags & XFS_DA_OP_JUSTCHECK)
L
Linus Torvalds 已提交
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 386 387 388 389 390 391 392 393 394 395 396
		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 已提交
397
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
398 399 400

	dp = args->dp;

C
Christoph Hellwig 已提交
401
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
402 403 404 405
	byteoff = (int)((char *)sfep - (char *)sfp);
	/*
	 * Grow the in-inode space.
	 */
406
	xfs_idata_realloc(dp, dp->d_ops->sf_entsize(sfp, args->namelen),
407
			  XFS_DATA_FORK);
L
Linus Torvalds 已提交
408 409 410
	/*
	 * Need to set up again due to realloc of the inode data.
	 */
C
Christoph Hellwig 已提交
411
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
412 413 414 415 416
	sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff);
	/*
	 * Fill in the new entry.
	 */
	sfep->namelen = args->namelen;
417
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
418
	memcpy(sfep->name, args->name, sfep->namelen);
419 420
	dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
	dp->d_ops->sf_put_ftype(sfep, args->filetype);
421

L
Linus Torvalds 已提交
422 423 424
	/*
	 * Update the header and inode.
	 */
C
Christoph Hellwig 已提交
425
	sfp->count++;
L
Linus Torvalds 已提交
426 427
#if XFS_BIG_INUMS
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM)
C
Christoph Hellwig 已提交
428
		sfp->i8count++;
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
#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 已提交
458
	xfs_dir2_sf_hdr_t	*oldsfp;	/* original shortform dir */
L
Linus Torvalds 已提交
459
	xfs_dir2_sf_entry_t	*sfep;		/* entry in new dir */
C
Christoph Hellwig 已提交
460
	xfs_dir2_sf_hdr_t	*sfp;		/* new shortform dir */
461
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
462 463 464 465 466

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

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

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

C
Christoph Hellwig 已提交
560
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
561
	size = dp->d_ops->data_entsize(args->namelen);
562
	offset = dp->d_ops->data_first_offset;
563
	sfep = xfs_dir2_sf_firstentry(sfp);
L
Linus Torvalds 已提交
564 565 566 567 568 569
	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 已提交
570
	for (i = 0; i < sfp->count; i++) {
L
Linus Torvalds 已提交
571
		if (!holefit)
572 573
			holefit = offset + size <= xfs_dir2_sf_get_offset(sfep);
		offset = xfs_dir2_sf_get_offset(sfep) +
574
			 dp->d_ops->data_entsize(sfep->namelen);
575
		sfep = dp->d_ops->sf_nextentry(sfp, sfep);
L
Linus Torvalds 已提交
576 577 578 579 580 581
	}
	/*
	 * Calculate data bytes used excluding the new entry, if this
	 * was a data block (block form directory).
	 */
	used = offset +
C
Christoph Hellwig 已提交
582
	       (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
L
Linus Torvalds 已提交
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
	       (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 已提交
628
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
629
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
630 631

	dp = args->dp;
632
	mp = dp->i_mount;
L
Linus Torvalds 已提交
633

C
Christoph Hellwig 已提交
634
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
635
	offset = dp->d_ops->data_first_offset;
636
	ino = dp->d_ops->sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
637 638
	i8count = ino > XFS_DIR2_MAX_SHORT_INUM;

639
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
C
Christoph Hellwig 已提交
640
	     i < sfp->count;
641
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
642
		ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset);
643
		ino = dp->d_ops->sf_get_ino(sfp, sfep);
L
Linus Torvalds 已提交
644 645
		i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
		offset =
646
			xfs_dir2_sf_get_offset(sfep) +
647
			dp->d_ops->data_entsize(sfep->namelen);
648
		ASSERT(dp->d_ops->sf_get_ftype(sfep) < XFS_DIR3_FT_MAX);
L
Linus Torvalds 已提交
649
	}
C
Christoph Hellwig 已提交
650
	ASSERT(i8count == sfp->i8count);
L
Linus Torvalds 已提交
651 652 653
	ASSERT(XFS_BIG_INUMS || i8count == 0);
	ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size);
	ASSERT(offset +
C
Christoph Hellwig 已提交
654
	       (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
655
	       (uint)sizeof(xfs_dir2_block_tail_t) <= mp->m_dirblksize);
L
Linus Torvalds 已提交
656 657 658 659 660 661 662 663 664 665 666 667 668
}
#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 已提交
669
	xfs_dir2_sf_hdr_t *sfp;		/* shortform structure */
L
Linus Torvalds 已提交
670 671
	int		size;		/* directory size */

C
Christoph Hellwig 已提交
672 673
	trace_xfs_dir2_sf_create(args);

L
Linus Torvalds 已提交
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
	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;
691
	size = xfs_dir2_sf_hdr_size(i8count);
L
Linus Torvalds 已提交
692 693 694 695 696 697 698
	/*
	 * Make a buffer for the data.
	 */
	xfs_idata_realloc(dp, size, XFS_DATA_FORK);
	/*
	 * Fill in the header,
	 */
C
Christoph Hellwig 已提交
699 700
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	sfp->i8count = i8count;
L
Linus Torvalds 已提交
701 702 703
	/*
	 * Now can put in the inode number, since i8count is set.
	 */
704
	dp->d_ops->sf_put_parent_ino(sfp, pino);
C
Christoph Hellwig 已提交
705
	sfp->count = 0;
L
Linus Torvalds 已提交
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
	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 */
722
	int			error;
L
Linus Torvalds 已提交
723
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
724
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
725
	enum xfs_dacmp		cmp;		/* comparison result */
726
	xfs_dir2_sf_entry_t	*ci_sfep;	/* case-insens. entry */
L
Linus Torvalds 已提交
727

C
Christoph Hellwig 已提交
728 729
	trace_xfs_dir2_sf_lookup(args);

L
Linus Torvalds 已提交
730 731 732 733 734 735 736 737 738 739 740 741 742
	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 已提交
743 744
	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 已提交
745 746 747 748 749
	/*
	 * Special case for .
	 */
	if (args->namelen == 1 && args->name[0] == '.') {
		args->inumber = dp->i_ino;
750
		args->cmpresult = XFS_CMP_EXACT;
751
		args->filetype = XFS_DIR3_FT_DIR;
L
Linus Torvalds 已提交
752 753 754 755 756 757 758
		return XFS_ERROR(EEXIST);
	}
	/*
	 * Special case for ..
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
759
		args->inumber = dp->d_ops->sf_get_parent_ino(sfp);
760
		args->cmpresult = XFS_CMP_EXACT;
761
		args->filetype = XFS_DIR3_FT_DIR;
L
Linus Torvalds 已提交
762 763 764 765 766
		return XFS_ERROR(EEXIST);
	}
	/*
	 * Loop over all the entries trying to match ours.
	 */
767
	ci_sfep = NULL;
C
Christoph Hellwig 已提交
768
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
769
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
770 771 772 773 774 775 776 777 778
		/*
		 * 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;
779 780
			args->inumber = dp->d_ops->sf_get_ino(sfp, sfep);
			args->filetype = dp->d_ops->sf_get_ftype(sfep);
781 782
			if (cmp == XFS_CMP_EXACT)
				return XFS_ERROR(EEXIST);
783
			ci_sfep = sfep;
L
Linus Torvalds 已提交
784 785
		}
	}
786
	ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
787 788
	/*
	 * Here, we can only be doing a lookup (not a rename or replace).
789
	 * If a case-insensitive match was not found, return ENOENT.
790
	 */
791 792 793 794 795
	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 已提交
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
}

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

C
Christoph Hellwig 已提交
814 815
	trace_xfs_dir2_sf_removename(args);

L
Linus Torvalds 已提交
816 817 818 819 820 821 822 823 824 825 826 827 828
	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 已提交
829 830
	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 已提交
831 832 833 834
	/*
	 * Loop over the old directory entries.
	 * Find the one we're deleting.
	 */
C
Christoph Hellwig 已提交
835
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
836
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
837 838
		if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
839
			ASSERT(dp->d_ops->sf_get_ino(sfp, sfep) ==
840
			       args->inumber);
L
Linus Torvalds 已提交
841 842 843 844 845 846
			break;
		}
	}
	/*
	 * Didn't find it.
	 */
C
Christoph Hellwig 已提交
847
	if (i == sfp->count)
L
Linus Torvalds 已提交
848 849 850 851 852
		return XFS_ERROR(ENOENT);
	/*
	 * Calculate sizes.
	 */
	byteoff = (int)((char *)sfep - (char *)sfp);
853
	entsize = dp->d_ops->sf_entsize(sfp, args->namelen);
L
Linus Torvalds 已提交
854 855 856 857 858 859 860 861 862 863
	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 已提交
864
	sfp->count--;
L
Linus Torvalds 已提交
865 866 867 868 869
	dp->i_d.di_size = newsize;
	/*
	 * Reallocate, making it smaller.
	 */
	xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK);
C
Christoph Hellwig 已提交
870
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
871 872 873 874 875
#if XFS_BIG_INUMS
	/*
	 * Are we changing inode number size?
	 */
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
C
Christoph Hellwig 已提交
876
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
877 878
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
879
			sfp->i8count--;
L
Linus Torvalds 已提交
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
	}
#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 已提交
903
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
904

C
Christoph Hellwig 已提交
905 906
	trace_xfs_dir2_sf_replace(args);

L
Linus Torvalds 已提交
907 908 909 910 911 912 913 914 915 916 917 918
	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 已提交
919 920
	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 已提交
921 922 923 924
#if XFS_BIG_INUMS
	/*
	 * New inode number is large, and need to convert to 8-byte inodes.
	 */
C
Christoph Hellwig 已提交
925
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
926 927 928 929 930
		int	error;			/* error return value */
		int	newsize;		/* new inode size */

		newsize =
			dp->i_df.if_bytes +
C
Christoph Hellwig 已提交
931
			(sfp->count + 1) *
L
Linus Torvalds 已提交
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
			((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 已提交
949
		sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
950 951 952 953 954 955 956 957 958 959
	} 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)
960
		ino = dp->d_ops->sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
961 962
		ASSERT(args->inumber != ino);
#endif
963
		dp->d_ops->sf_put_parent_ino(sfp, args->inumber);
L
Linus Torvalds 已提交
964 965 966 967 968
	}
	/*
	 * Normal entry, look for the name.
	 */
	else {
969
		for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
970
		     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
971 972
			if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
L
Linus Torvalds 已提交
973
#if XFS_BIG_INUMS || defined(DEBUG)
974
				ino = dp->d_ops->sf_get_ino(sfp, sfep);
L
Linus Torvalds 已提交
975 976
				ASSERT(args->inumber != ino);
#endif
977 978
				dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
				dp->d_ops->sf_put_ftype(sfep, args->filetype);
L
Linus Torvalds 已提交
979 980 981 982 983 984
				break;
			}
		}
		/*
		 * Didn't find it.
		 */
C
Christoph Hellwig 已提交
985
		if (i == sfp->count) {
986
			ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
L
Linus Torvalds 已提交
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
#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 已提交
1003
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
1004 1005
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
1006
			sfp->i8count--;
L
Linus Torvalds 已提交
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
	}
	/*
	 * 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 已提交
1017
		ASSERT(sfp->i8count != 0);
L
Linus Torvalds 已提交
1018
		if (!i8elevated)
C
Christoph Hellwig 已提交
1019
			sfp->i8count++;
L
Linus Torvalds 已提交
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
	}
#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 已提交
1041
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1042 1043
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1044
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
1045
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
1046

C
Christoph Hellwig 已提交
1047 1048
	trace_xfs_dir2_sf_toino4(args);

L
Linus Torvalds 已提交
1049
	dp = args->dp;
1050
	mp = dp->i_mount;
L
Linus Torvalds 已提交
1051 1052 1053 1054 1055 1056 1057 1058

	/*
	 * 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 已提交
1059 1060
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 1);
L
Linus Torvalds 已提交
1061 1062 1063 1064 1065 1066
	memcpy(buf, oldsfp, oldsize);
	/*
	 * Compute the new inode size.
	 */
	newsize =
		oldsize -
C
Christoph Hellwig 已提交
1067
		(oldsfp->count + 1) *
L
Linus Torvalds 已提交
1068 1069 1070 1071 1072 1073
		((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 已提交
1074 1075
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1076 1077 1078
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1079 1080
	sfp->count = oldsfp->count;
	sfp->i8count = 0;
1081
	dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1082 1083 1084
	/*
	 * Copy the entries field by field.
	 */
1085 1086
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1087
	     i < sfp->count;
1088 1089
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep),
		  oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1090 1091 1092
		sfep->namelen = oldsfep->namelen;
		sfep->offset = oldsfep->offset;
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1093 1094 1095
		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 已提交
1096 1097 1098 1099
	}
	/*
	 * Clean up the inode.
	 */
1100
	kmem_free(buf);
L
Linus Torvalds 已提交
1101 1102 1103 1104 1105
	dp->i_d.di_size = newsize;
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}

/*
1106 1107 1108
 * 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 已提交
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
 */
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 已提交
1119
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1120 1121
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1122
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
1123
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
1124

C
Christoph Hellwig 已提交
1125 1126
	trace_xfs_dir2_sf_toino8(args);

L
Linus Torvalds 已提交
1127
	dp = args->dp;
1128
	mp = dp->i_mount;
L
Linus Torvalds 已提交
1129 1130 1131 1132 1133 1134 1135 1136

	/*
	 * 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 已提交
1137 1138
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 0);
L
Linus Torvalds 已提交
1139 1140
	memcpy(buf, oldsfp, oldsize);
	/*
1141
	 * Compute the new inode size (nb: entry count + 1 for parent)
L
Linus Torvalds 已提交
1142 1143 1144
	 */
	newsize =
		oldsize +
C
Christoph Hellwig 已提交
1145
		(oldsfp->count + 1) *
L
Linus Torvalds 已提交
1146 1147 1148 1149 1150 1151
		((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 已提交
1152 1153
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1154 1155 1156
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1157 1158
	sfp->count = oldsfp->count;
	sfp->i8count = 1;
1159
	dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1160 1161 1162
	/*
	 * Copy the entries field by field.
	 */
1163 1164
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1165
	     i < sfp->count;
1166 1167
	     i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep),
		  oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1168 1169 1170
		sfep->namelen = oldsfep->namelen;
		sfep->offset = oldsfep->offset;
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1171 1172 1173
		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 已提交
1174 1175 1176 1177
	}
	/*
	 * Clean up the inode.
	 */
1178
	kmem_free(buf);
L
Linus Torvalds 已提交
1179 1180 1181 1182
	dp->i_d.di_size = newsize;
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}
#endif	/* XFS_BIG_INUMS */