xfs_dir2.c 16.1 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3
 * Copyright (c) 2000-2001,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
#include "xfs_types.h"
21
#include "xfs_bit.h"
L
Linus Torvalds 已提交
22
#include "xfs_log.h"
23
#include "xfs_inum.h"
L
Linus Torvalds 已提交
24 25 26 27
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_mount.h"
28
#include "xfs_da_btree.h"
L
Linus Torvalds 已提交
29
#include "xfs_bmap_btree.h"
30
#include "xfs_alloc_btree.h"
L
Linus Torvalds 已提交
31 32
#include "xfs_dinode.h"
#include "xfs_inode.h"
33
#include "xfs_inode_item.h"
L
Linus Torvalds 已提交
34
#include "xfs_bmap.h"
C
Christoph Hellwig 已提交
35 36 37
#include "xfs_dir2.h"
#include "xfs_dir2_format.h"
#include "xfs_dir2_priv.h"
L
Linus Torvalds 已提交
38
#include "xfs_error.h"
D
David Chinner 已提交
39
#include "xfs_vnodeops.h"
C
Christoph Hellwig 已提交
40
#include "xfs_trace.h"
L
Linus Torvalds 已提交
41

42
struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2};
L
Linus Torvalds 已提交
43

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*
 * ASCII case-insensitive (ie. A-Z) support for directories that was
 * used in IRIX.
 */
STATIC xfs_dahash_t
xfs_ascii_ci_hashname(
	struct xfs_name	*name)
{
	xfs_dahash_t	hash;
	int		i;

	for (i = 0, hash = 0; i < name->len; i++)
		hash = tolower(name->name[i]) ^ rol32(hash, 7);

	return hash;
}

STATIC enum xfs_dacmp
xfs_ascii_ci_compname(
	struct xfs_da_args *args,
64 65
	const unsigned char *name,
	int		len)
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
{
	enum xfs_dacmp	result;
	int		i;

	if (args->namelen != len)
		return XFS_CMP_DIFFERENT;

	result = XFS_CMP_EXACT;
	for (i = 0; i < len; i++) {
		if (args->name[i] == name[i])
			continue;
		if (tolower(args->name[i]) != tolower(name[i]))
			return XFS_CMP_DIFFERENT;
		result = XFS_CMP_CASE;
	}

	return result;
}

static struct xfs_nameops xfs_ascii_ci_nameops = {
	.hashname	= xfs_ascii_ci_hashname,
	.compname	= xfs_ascii_ci_compname,
};

90 91 92
void
xfs_dir_mount(
	xfs_mount_t	*mp)
L
Linus Torvalds 已提交
93
{
94
	ASSERT(xfs_sb_version_hasdirv2(&mp->m_sb));
L
Linus Torvalds 已提交
95 96 97 98
	ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <=
	       XFS_MAX_BLOCKSIZE);
	mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog);
	mp->m_dirblkfsbs = 1 << mp->m_sb.sb_dirblklog;
99 100 101
	mp->m_dirdatablk = xfs_dir2_db_to_da(mp, XFS_DIR2_DATA_FIRSTDB(mp));
	mp->m_dirleafblk = xfs_dir2_db_to_da(mp, XFS_DIR2_LEAF_FIRSTDB(mp));
	mp->m_dirfreeblk = xfs_dir2_db_to_da(mp, XFS_DIR2_FREE_FIRSTDB(mp));
L
Linus Torvalds 已提交
102 103 104 105 106 107 108
	mp->m_attr_node_ents =
		(mp->m_sb.sb_blocksize - (uint)sizeof(xfs_da_node_hdr_t)) /
		(uint)sizeof(xfs_da_node_entry_t);
	mp->m_dir_node_ents =
		(mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
		(uint)sizeof(xfs_da_node_entry_t);
	mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
109 110 111 112
	if (xfs_sb_version_hasasciici(&mp->m_sb))
		mp->m_dirnameops = &xfs_ascii_ci_nameops;
	else
		mp->m_dirnameops = &xfs_default_nameops;
L
Linus Torvalds 已提交
113 114 115 116 117
}

/*
 * Return 1 if directory contains only "." and "..".
 */
118 119 120
int
xfs_dir_isempty(
	xfs_inode_t	*dp)
L
Linus Torvalds 已提交
121
{
C
Christoph Hellwig 已提交
122
	xfs_dir2_sf_hdr_t	*sfp;
L
Linus Torvalds 已提交
123 124

	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
125
	if (dp->i_d.di_size == 0)	/* might happen during shutdown. */
L
Linus Torvalds 已提交
126 127 128
		return 1;
	if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
		return 0;
C
Christoph Hellwig 已提交
129 130
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	return !sfp->count;
L
Linus Torvalds 已提交
131 132
}

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/*
 * Validate a given inode number.
 */
int
xfs_dir_ino_validate(
	xfs_mount_t	*mp,
	xfs_ino_t	ino)
{
	xfs_agblock_t	agblkno;
	xfs_agino_t	agino;
	xfs_agnumber_t	agno;
	int		ino_ok;
	int		ioff;

	agno = XFS_INO_TO_AGNO(mp, ino);
	agblkno = XFS_INO_TO_AGBNO(mp, ino);
	ioff = XFS_INO_TO_OFFSET(mp, ino);
	agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff);
	ino_ok =
		agno < mp->m_sb.sb_agcount &&
		agblkno < mp->m_sb.sb_agblocks &&
		agblkno != 0 &&
		ioff < (1 << mp->m_sb.sb_inopblog) &&
		XFS_AGINO_TO_INO(mp, agno, agino) == ino;
	if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE,
			XFS_RANDOM_DIR_INO_VALIDATE))) {
159
		xfs_warn(mp, "Invalid inode number 0x%Lx",
160 161 162 163 164 165 166
				(unsigned long long) ino);
		XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
		return XFS_ERROR(EFSCORRUPTED);
	}
	return 0;
}

L
Linus Torvalds 已提交
167 168 169
/*
 * Initialize a directory with its "." and ".." entries.
 */
170 171 172 173 174
int
xfs_dir_init(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
	xfs_inode_t	*pdp)
L
Linus Torvalds 已提交
175
{
176 177
	xfs_da_args_t	args;
	int		error;
L
Linus Torvalds 已提交
178 179 180 181 182

	memset((char *)&args, 0, sizeof(args));
	args.dp = dp;
	args.trans = tp;
	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
183
	if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino)))
L
Linus Torvalds 已提交
184 185 186 187 188 189 190
		return error;
	return xfs_dir2_sf_create(&args, pdp->i_ino);
}

/*
  Enter a name in a directory.
 */
191 192 193 194
int
xfs_dir_createname(
	xfs_trans_t		*tp,
	xfs_inode_t		*dp,
195
	struct xfs_name		*name,
L
Linus Torvalds 已提交
196 197 198 199 200
	xfs_ino_t		inum,		/* new entry inode number */
	xfs_fsblock_t		*first,		/* bmap's firstblock */
	xfs_bmap_free_t		*flist,		/* bmap's freeblock list */
	xfs_extlen_t		total)		/* bmap's total block count */
{
201 202
	xfs_da_args_t		args;
	int			rval;
L
Linus Torvalds 已提交
203 204 205
	int			v;		/* type-checking value */

	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
206
	if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
L
Linus Torvalds 已提交
207 208
		return rval;
	XFS_STATS_INC(xs_dir_create);
209

210
	memset(&args, 0, sizeof(xfs_da_args_t));
211 212
	args.name = name->name;
	args.namelen = name->len;
213
	args.hashval = dp->i_mount->m_dirnameops->hashname(name);
L
Linus Torvalds 已提交
214 215 216 217 218 219 220
	args.inumber = inum;
	args.dp = dp;
	args.firstblock = first;
	args.flist = flist;
	args.total = total;
	args.whichfork = XFS_DATA_FORK;
	args.trans = tp;
221
	args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
222

L
Linus Torvalds 已提交
223 224
	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_addname(&args);
225
	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
L
Linus Torvalds 已提交
226
		return rval;
227
	else if (v)
L
Linus Torvalds 已提交
228
		rval = xfs_dir2_block_addname(&args);
229
	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
L
Linus Torvalds 已提交
230
		return rval;
231
	else if (v)
L
Linus Torvalds 已提交
232 233 234 235 236 237
		rval = xfs_dir2_leaf_addname(&args);
	else
		rval = xfs_dir2_node_addname(&args);
	return rval;
}

238 239 240 241 242 243 244
/*
 * If doing a CI lookup and case-insensitive match, dup actual name into
 * args.value. Return EEXIST for success (ie. name found) or an error.
 */
int
xfs_dir_cilookup_result(
	struct xfs_da_args *args,
245
	const unsigned char *name,
246 247 248 249 250 251 252 253
	int		len)
{
	if (args->cmpresult == XFS_CMP_DIFFERENT)
		return ENOENT;
	if (args->cmpresult != XFS_CMP_CASE ||
					!(args->op_flags & XFS_DA_OP_CILOOKUP))
		return EEXIST;

254
	args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL);
255 256 257 258 259 260 261 262
	if (!args->value)
		return ENOMEM;

	memcpy(args->value, name, len);
	args->valuelen = len;
	return EEXIST;
}

L
Linus Torvalds 已提交
263 264
/*
 * Lookup a name in a directory, give back the inode number.
265 266
 * If ci_name is not NULL, returns the actual name in ci_name if it differs
 * to name, or ci_name->name is set to NULL for an exact match.
L
Linus Torvalds 已提交
267
 */
268

269 270 271 272
int
xfs_dir_lookup(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
273
	struct xfs_name	*name,
274 275
	xfs_ino_t	*inum,		/* out: inode number */
	struct xfs_name *ci_name)	/* out: actual name if CI match */
L
Linus Torvalds 已提交
276
{
277 278
	xfs_da_args_t	args;
	int		rval;
L
Linus Torvalds 已提交
279 280 281 282 283
	int		v;		/* type-checking value */

	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
	XFS_STATS_INC(xs_dir_lookup);

284
	memset(&args, 0, sizeof(xfs_da_args_t));
285 286
	args.name = name->name;
	args.namelen = name->len;
287
	args.hashval = dp->i_mount->m_dirnameops->hashname(name);
L
Linus Torvalds 已提交
288 289 290
	args.dp = dp;
	args.whichfork = XFS_DATA_FORK;
	args.trans = tp;
291
	args.op_flags = XFS_DA_OP_OKNOENT;
292 293
	if (ci_name)
		args.op_flags |= XFS_DA_OP_CILOOKUP;
294

L
Linus Torvalds 已提交
295 296
	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_lookup(&args);
297
	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
L
Linus Torvalds 已提交
298
		return rval;
299
	else if (v)
L
Linus Torvalds 已提交
300
		rval = xfs_dir2_block_lookup(&args);
301
	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
L
Linus Torvalds 已提交
302
		return rval;
303
	else if (v)
L
Linus Torvalds 已提交
304 305 306 307 308
		rval = xfs_dir2_leaf_lookup(&args);
	else
		rval = xfs_dir2_node_lookup(&args);
	if (rval == EEXIST)
		rval = 0;
309
	if (!rval) {
L
Linus Torvalds 已提交
310
		*inum = args.inumber;
311 312 313 314 315
		if (ci_name) {
			ci_name->name = args.value;
			ci_name->len = args.valuelen;
		}
	}
L
Linus Torvalds 已提交
316 317 318 319 320 321
	return rval;
}

/*
 * Remove an entry from a directory.
 */
322 323 324 325
int
xfs_dir_removename(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
326
	struct xfs_name	*name,
327
	xfs_ino_t	ino,
L
Linus Torvalds 已提交
328 329 330 331
	xfs_fsblock_t	*first,		/* bmap's firstblock */
	xfs_bmap_free_t	*flist,		/* bmap's freeblock list */
	xfs_extlen_t	total)		/* bmap's total block count */
{
332 333
	xfs_da_args_t	args;
	int		rval;
L
Linus Torvalds 已提交
334 335 336 337
	int		v;		/* type-checking value */

	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
	XFS_STATS_INC(xs_dir_remove);
338

339
	memset(&args, 0, sizeof(xfs_da_args_t));
340 341
	args.name = name->name;
	args.namelen = name->len;
342
	args.hashval = dp->i_mount->m_dirnameops->hashname(name);
L
Linus Torvalds 已提交
343 344 345 346 347 348 349
	args.inumber = ino;
	args.dp = dp;
	args.firstblock = first;
	args.flist = flist;
	args.total = total;
	args.whichfork = XFS_DATA_FORK;
	args.trans = tp;
350

L
Linus Torvalds 已提交
351 352
	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_removename(&args);
353
	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
L
Linus Torvalds 已提交
354
		return rval;
355
	else if (v)
L
Linus Torvalds 已提交
356
		rval = xfs_dir2_block_removename(&args);
357
	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
L
Linus Torvalds 已提交
358
		return rval;
359
	else if (v)
L
Linus Torvalds 已提交
360 361 362 363 364 365 366 367 368
		rval = xfs_dir2_leaf_removename(&args);
	else
		rval = xfs_dir2_node_removename(&args);
	return rval;
}

/*
 * Read a directory.
 */
369
int
C
Christoph Hellwig 已提交
370
xfs_readdir(
371
	xfs_inode_t	*dp,
C
Christoph Hellwig 已提交
372 373 374 375
	void		*dirent,
	size_t		bufsize,
	xfs_off_t	*offset,
	filldir_t	filldir)
L
Linus Torvalds 已提交
376 377 378 379
{
	int		rval;		/* return value */
	int		v;		/* type-checking value */

C
Christoph Hellwig 已提交
380
	trace_xfs_readdir(dp);
C
Christoph Hellwig 已提交
381 382 383 384

	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
		return XFS_ERROR(EIO);

L
Linus Torvalds 已提交
385 386 387 388
	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
	XFS_STATS_INC(xs_dir_getdents);

	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
C
Christoph Hellwig 已提交
389 390
		rval = xfs_dir2_sf_getdents(dp, dirent, offset, filldir);
	else if ((rval = xfs_dir2_isblock(NULL, dp, &v)))
L
Linus Torvalds 已提交
391
		;
392
	else if (v)
C
Christoph Hellwig 已提交
393
		rval = xfs_dir2_block_getdents(dp, dirent, offset, filldir);
L
Linus Torvalds 已提交
394
	else
C
Christoph Hellwig 已提交
395 396
		rval = xfs_dir2_leaf_getdents(dp, dirent, bufsize, offset,
					      filldir);
L
Linus Torvalds 已提交
397 398 399 400 401 402
	return rval;
}

/*
 * Replace the inode number of a directory entry.
 */
403 404 405 406
int
xfs_dir_replace(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
407
	struct xfs_name	*name,		/* name of entry to replace */
L
Linus Torvalds 已提交
408 409 410 411 412
	xfs_ino_t	inum,		/* new inode number */
	xfs_fsblock_t	*first,		/* bmap's firstblock */
	xfs_bmap_free_t	*flist,		/* bmap's freeblock list */
	xfs_extlen_t	total)		/* bmap's total block count */
{
413 414
	xfs_da_args_t	args;
	int		rval;
L
Linus Torvalds 已提交
415 416 417 418
	int		v;		/* type-checking value */

	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);

419
	if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
L
Linus Torvalds 已提交
420
		return rval;
421

422
	memset(&args, 0, sizeof(xfs_da_args_t));
423 424
	args.name = name->name;
	args.namelen = name->len;
425
	args.hashval = dp->i_mount->m_dirnameops->hashname(name);
L
Linus Torvalds 已提交
426 427 428 429 430 431 432
	args.inumber = inum;
	args.dp = dp;
	args.firstblock = first;
	args.flist = flist;
	args.total = total;
	args.whichfork = XFS_DATA_FORK;
	args.trans = tp;
433

L
Linus Torvalds 已提交
434 435
	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_replace(&args);
436
	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
L
Linus Torvalds 已提交
437
		return rval;
438
	else if (v)
L
Linus Torvalds 已提交
439
		rval = xfs_dir2_block_replace(&args);
440
	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
L
Linus Torvalds 已提交
441
		return rval;
442
	else if (v)
L
Linus Torvalds 已提交
443 444 445 446 447 448 449 450
		rval = xfs_dir2_leaf_replace(&args);
	else
		rval = xfs_dir2_node_replace(&args);
	return rval;
}

/*
 * See if this entry can be added to the directory without allocating space.
451
 * First checks that the caller couldn't reserve enough space (resblks = 0).
L
Linus Torvalds 已提交
452
 */
453 454 455 456
int
xfs_dir_canenter(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
457 458
	struct xfs_name	*name,		/* name of entry to add */
	uint		resblks)
L
Linus Torvalds 已提交
459
{
460 461
	xfs_da_args_t	args;
	int		rval;
L
Linus Torvalds 已提交
462 463
	int		v;		/* type-checking value */

464 465 466
	if (resblks)
		return 0;

L
Linus Torvalds 已提交
467
	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
468

469
	memset(&args, 0, sizeof(xfs_da_args_t));
470 471
	args.name = name->name;
	args.namelen = name->len;
472
	args.hashval = dp->i_mount->m_dirnameops->hashname(name);
L
Linus Torvalds 已提交
473 474 475
	args.dp = dp;
	args.whichfork = XFS_DATA_FORK;
	args.trans = tp;
476 477
	args.op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
							XFS_DA_OP_OKNOENT;
478

L
Linus Torvalds 已提交
479 480
	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_addname(&args);
481
	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
L
Linus Torvalds 已提交
482
		return rval;
483
	else if (v)
L
Linus Torvalds 已提交
484
		rval = xfs_dir2_block_addname(&args);
485
	else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
L
Linus Torvalds 已提交
486
		return rval;
487
	else if (v)
L
Linus Torvalds 已提交
488 489 490 491 492 493 494 495 496 497 498 499
		rval = xfs_dir2_leaf_addname(&args);
	else
		rval = xfs_dir2_node_addname(&args);
	return rval;
}

/*
 * Utility routines.
 */

/*
 * Add a block to the directory.
500 501 502
 *
 * This routine is for data and free blocks, not leaf/node blocks which are
 * handled by xfs_da_grow_inode.
L
Linus Torvalds 已提交
503
 */
504
int
L
Linus Torvalds 已提交
505
xfs_dir2_grow_inode(
506 507 508
	struct xfs_da_args	*args,
	int			space,	/* v2 dir's space XFS_DIR2_xxx_SPACE */
	xfs_dir2_db_t		*dbp)	/* out: block number added */
L
Linus Torvalds 已提交
509
{
510 511 512 513 514
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
	xfs_fileoff_t		bno;	/* directory offset of new block */
	int			count;	/* count of filesystem blocks */
	int			error;
L
Linus Torvalds 已提交
515

C
Christoph Hellwig 已提交
516 517
	trace_xfs_dir2_grow_inode(args, space);

L
Linus Torvalds 已提交
518 519 520 521 522
	/*
	 * Set lowest possible block in the space requested.
	 */
	bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE);
	count = mp->m_dirblkfsbs;
523 524 525

	error = xfs_da_grow_inode_int(args, &bno, count);
	if (error)
L
Linus Torvalds 已提交
526
		return error;
527

528
	*dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno);
529

L
Linus Torvalds 已提交
530 531 532 533 534 535 536 537 538
	/*
	 * Update file's size if this is the data space and it grew.
	 */
	if (space == XFS_DIR2_DATA_SPACE) {
		xfs_fsize_t	size;		/* directory file (data) size */

		size = XFS_FSB_TO_B(mp, bno + count);
		if (size > dp->i_d.di_size) {
			dp->i_d.di_size = size;
539
			xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
L
Linus Torvalds 已提交
540 541 542 543 544 545 546 547
		}
	}
	return 0;
}

/*
 * See if the directory is a single-block form directory.
 */
548
int
L
Linus Torvalds 已提交
549
xfs_dir2_isblock(
550 551
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
L
Linus Torvalds 已提交
552 553 554
	int		*vp)		/* out: 1 is block, 0 is not block */
{
	xfs_fileoff_t	last;		/* last file offset */
555 556
	xfs_mount_t	*mp;
	int		rval;
L
Linus Torvalds 已提交
557 558

	mp = dp->i_mount;
559
	if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK)))
L
Linus Torvalds 已提交
560 561 562 563 564 565 566 567 568 569
		return rval;
	rval = XFS_FSB_TO_B(mp, last) == mp->m_dirblksize;
	ASSERT(rval == 0 || dp->i_d.di_size == mp->m_dirblksize);
	*vp = rval;
	return 0;
}

/*
 * See if the directory is a single-leaf form directory.
 */
570
int
L
Linus Torvalds 已提交
571
xfs_dir2_isleaf(
572 573
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
L
Linus Torvalds 已提交
574 575 576
	int		*vp)		/* out: 1 is leaf, 0 is not leaf */
{
	xfs_fileoff_t	last;		/* last file offset */
577 578
	xfs_mount_t	*mp;
	int		rval;
L
Linus Torvalds 已提交
579 580

	mp = dp->i_mount;
581
	if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK)))
L
Linus Torvalds 已提交
582 583 584 585 586 587 588 589 590 591 592 593
		return rval;
	*vp = last == mp->m_dirleafblk + (1 << mp->m_sb.sb_dirblklog);
	return 0;
}

/*
 * Remove the given block from the directory.
 * This routine is used for data and free blocks, leaf/node are done
 * by xfs_da_shrink_inode.
 */
int
xfs_dir2_shrink_inode(
594 595 596
	xfs_da_args_t	*args,
	xfs_dir2_db_t	db,
	xfs_dabuf_t	*bp)
L
Linus Torvalds 已提交
597 598 599 600
{
	xfs_fileoff_t	bno;		/* directory file offset */
	xfs_dablk_t	da;		/* directory file offset */
	int		done;		/* bunmap is finished */
601 602 603 604
	xfs_inode_t	*dp;
	int		error;
	xfs_mount_t	*mp;
	xfs_trans_t	*tp;
L
Linus Torvalds 已提交
605

C
Christoph Hellwig 已提交
606 607
	trace_xfs_dir2_shrink_inode(args, db);

L
Linus Torvalds 已提交
608 609 610
	dp = args->dp;
	mp = dp->i_mount;
	tp = args->trans;
611
	da = xfs_dir2_db_to_da(mp, db);
L
Linus Torvalds 已提交
612 613 614 615 616
	/*
	 * Unmap the fsblock(s).
	 */
	if ((error = xfs_bunmapi(tp, dp, da, mp->m_dirblkfsbs,
			XFS_BMAPI_METADATA, 0, args->firstblock, args->flist,
617
			&done))) {
L
Linus Torvalds 已提交
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
		/*
		 * ENOSPC actually can happen if we're in a removename with
		 * no space reservation, and the resulting block removal
		 * would cause a bmap btree split or conversion from extents
		 * to btree.  This can only happen for un-fragmented
		 * directory blocks, since you need to be punching out
		 * the middle of an extent.
		 * In this case we need to leave the block in the file,
		 * and not binval it.
		 * So the block has to be in a consistent empty state
		 * and appropriately logged.
		 * We don't free up the buffer, the caller can tell it
		 * hasn't happened since it got an error back.
		 */
		return error;
	}
	ASSERT(done);
	/*
	 * Invalidate the buffer from the transaction.
	 */
	xfs_da_binval(tp, bp);
	/*
	 * If it's not a data block, we're done.
	 */
	if (db >= XFS_DIR2_LEAF_FIRSTDB(mp))
		return 0;
	/*
	 * If the block isn't the last one in the directory, we're done.
	 */
647
	if (dp->i_d.di_size > xfs_dir2_db_off_to_byte(mp, db + 1, 0))
L
Linus Torvalds 已提交
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
		return 0;
	bno = da;
	if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) {
		/*
		 * This can't really happen unless there's kernel corruption.
		 */
		return error;
	}
	if (db == mp->m_dirdatablk)
		ASSERT(bno == 0);
	else
		ASSERT(bno > 0);
	/*
	 * Set the size to the new last block.
	 */
	dp->i_d.di_size = XFS_FSB_TO_B(mp, bno);
	xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
	return 0;
}