xfs_itable.c 18.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2 3
 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
 * All Rights Reserved.
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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.
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
17 18
 */
#include "xfs.h"
19
#include "xfs_fs.h"
20
#include "xfs_shared.h"
21
#include "xfs_format.h"
22 23
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
24
#include "xfs_inum.h"
Linus Torvalds's avatar
Linus Torvalds committed
25
#include "xfs_sb.h"
26
#include "xfs_ag.h"
Linus Torvalds's avatar
Linus Torvalds committed
27 28
#include "xfs_mount.h"
#include "xfs_inode.h"
29
#include "xfs_btree.h"
Linus Torvalds's avatar
Linus Torvalds committed
30
#include "xfs_ialloc.h"
31
#include "xfs_ialloc_btree.h"
Linus Torvalds's avatar
Linus Torvalds committed
32 33
#include "xfs_itable.h"
#include "xfs_error.h"
Christoph Hellwig's avatar
Christoph Hellwig committed
34
#include "xfs_trace.h"
Dave Chinner's avatar
Dave Chinner committed
35
#include "xfs_icache.h"
36
#include "xfs_dinode.h"
Linus Torvalds's avatar
Linus Torvalds committed
37

38
STATIC int
39 40 41 42 43
xfs_internal_inum(
	xfs_mount_t	*mp,
	xfs_ino_t	ino)
{
	return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
44
		(xfs_sb_version_hasquota(&mp->m_sb) &&
45
		 xfs_is_quota_inode(&mp->m_sb, ino)));
46 47
}

48 49 50 51 52 53 54 55 56 57 58 59 60
/*
 * Return stat information for one inode.
 * Return 0 if ok, else errno.
 */
int
xfs_bulkstat_one_int(
	struct xfs_mount	*mp,		/* mount point for filesystem */
	xfs_ino_t		ino,		/* inode to get data for */
	void __user		*buffer,	/* buffer to place output in */
	int			ubsize,		/* size of buffer */
	bulkstat_one_fmt_pf	formatter,	/* formatter, copy to user */
	int			*ubused,	/* bytes used by me */
	int			*stat)		/* BULKSTAT_RV_... */
Linus Torvalds's avatar
Linus Torvalds committed
61
{
62 63 64 65 66 67 68 69 70 71 72 73 74
	struct xfs_icdinode	*dic;		/* dinode core info pointer */
	struct xfs_inode	*ip;		/* incore inode pointer */
	struct xfs_bstat	*buf;		/* return buffer */
	int			error = 0;	/* error value */

	*stat = BULKSTAT_RV_NOTHING;

	if (!buffer || xfs_internal_inum(mp, ino))
		return XFS_ERROR(EINVAL);

	buf = kmem_alloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL);
	if (!buf)
		return XFS_ERROR(ENOMEM);
Linus Torvalds's avatar
Linus Torvalds committed
75

76
	error = xfs_iget(mp, NULL, ino,
77 78
			 (XFS_IGET_DONTCACHE | XFS_IGET_UNTRUSTED),
			 XFS_ILOCK_SHARED, &ip);
Linus Torvalds's avatar
Linus Torvalds committed
79 80
	if (error) {
		*stat = BULKSTAT_RV_NOTHING;
81
		goto out_free;
Linus Torvalds's avatar
Linus Torvalds committed
82 83 84
	}

	ASSERT(ip != NULL);
85
	ASSERT(ip->i_imap.im_blkno != 0);
Linus Torvalds's avatar
Linus Torvalds committed
86 87 88 89 90 91 92

	dic = &ip->i_d;

	/* xfs_iget returns the following without needing
	 * further change.
	 */
	buf->bs_nlink = dic->di_nlink;
93 94
	buf->bs_projid_lo = dic->di_projid_lo;
	buf->bs_projid_hi = dic->di_projid_hi;
Linus Torvalds's avatar
Linus Torvalds committed
95 96 97 98 99
	buf->bs_ino = ino;
	buf->bs_mode = dic->di_mode;
	buf->bs_uid = dic->di_uid;
	buf->bs_gid = dic->di_gid;
	buf->bs_size = dic->di_size;
100 101 102 103 104 105
	buf->bs_atime.tv_sec = dic->di_atime.t_sec;
	buf->bs_atime.tv_nsec = dic->di_atime.t_nsec;
	buf->bs_mtime.tv_sec = dic->di_mtime.t_sec;
	buf->bs_mtime.tv_nsec = dic->di_mtime.t_nsec;
	buf->bs_ctime.tv_sec = dic->di_ctime.t_sec;
	buf->bs_ctime.tv_nsec = dic->di_ctime.t_nsec;
Linus Torvalds's avatar
Linus Torvalds committed
106 107 108 109 110 111 112 113
	buf->bs_xflags = xfs_ip2xflags(ip);
	buf->bs_extsize = dic->di_extsize << mp->m_sb.sb_blocklog;
	buf->bs_extents = dic->di_nextents;
	buf->bs_gen = dic->di_gen;
	memset(buf->bs_pad, 0, sizeof(buf->bs_pad));
	buf->bs_dmevmask = dic->di_dmevmask;
	buf->bs_dmstate = dic->di_dmstate;
	buf->bs_aextents = dic->di_anextents;
114
	buf->bs_forkoff = XFS_IFORK_BOFF(ip);
Linus Torvalds's avatar
Linus Torvalds committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

	switch (dic->di_format) {
	case XFS_DINODE_FMT_DEV:
		buf->bs_rdev = ip->i_df.if_u2.if_rdev;
		buf->bs_blksize = BLKDEV_IOSIZE;
		buf->bs_blocks = 0;
		break;
	case XFS_DINODE_FMT_LOCAL:
	case XFS_DINODE_FMT_UUID:
		buf->bs_rdev = 0;
		buf->bs_blksize = mp->m_sb.sb_blocksize;
		buf->bs_blocks = 0;
		break;
	case XFS_DINODE_FMT_EXTENTS:
	case XFS_DINODE_FMT_BTREE:
		buf->bs_rdev = 0;
		buf->bs_blksize = mp->m_sb.sb_blocksize;
		buf->bs_blocks = dic->di_nblocks + ip->i_delayed_blks;
		break;
	}
Christoph Hellwig's avatar
Christoph Hellwig committed
135 136
	xfs_iunlock(ip, XFS_ILOCK_SHARED);
	IRELE(ip);
Linus Torvalds's avatar
Linus Torvalds committed
137

138
	error = formatter(buffer, ubsize, ubused, buf);
Linus Torvalds's avatar
Linus Torvalds committed
139

140 141
	if (!error)
		*stat = BULKSTAT_RV_DIDONE;
Linus Torvalds's avatar
Linus Torvalds committed
142

143 144 145
 out_free:
	kmem_free(buf);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
146 147
}

148
/* Return 0 on success or positive error */
149 150 151
STATIC int
xfs_bulkstat_one_fmt(
	void			__user *ubuffer,
152 153
	int			ubsize,
	int			*ubused,
154 155
	const xfs_bstat_t	*buffer)
{
156 157
	if (ubsize < sizeof(*buffer))
		return XFS_ERROR(ENOMEM);
158
	if (copy_to_user(ubuffer, buffer, sizeof(*buffer)))
159 160 161 162
		return XFS_ERROR(EFAULT);
	if (ubused)
		*ubused = sizeof(*buffer);
	return 0;
163 164
}

165 166 167 168 169 170 171 172 173 174
int
xfs_bulkstat_one(
	xfs_mount_t	*mp,		/* mount point for filesystem */
	xfs_ino_t	ino,		/* inode number to get data for */
	void		__user *buffer,	/* buffer to place output in */
	int		ubsize,		/* size of buffer */
	int		*ubused,	/* bytes used by me */
	int		*stat)		/* BULKSTAT_RV_... */
{
	return xfs_bulkstat_one_int(mp, ino, buffer, ubsize,
175
				    xfs_bulkstat_one_fmt, ubused, stat);
176 177
}

178 179
#define XFS_BULKSTAT_UBLEFT(ubleft)	((ubleft) >= statstruct_size)

Linus Torvalds's avatar
Linus Torvalds committed
180 181 182 183 184 185 186 187 188 189 190
/*
 * Return stat information in bulk (by-inode) for the filesystem.
 */
int					/* error status */
xfs_bulkstat(
	xfs_mount_t		*mp,	/* mount point for filesystem */
	xfs_ino_t		*lastinop, /* last inode returned */
	int			*ubcountp, /* size of buffer/count returned */
	bulkstat_one_pf		formatter, /* func that'd fill a single buf */
	size_t			statstruct_size, /* sizeof struct filling */
	char			__user *ubuffer, /* buffer with inode stats */
191
	int			*done)	/* 1 if there are more stats to get */
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205
{
	xfs_agblock_t		agbno=0;/* allocation group block number */
	xfs_buf_t		*agbp;	/* agi header buffer */
	xfs_agi_t		*agi;	/* agi header data */
	xfs_agino_t		agino;	/* inode # in allocation group */
	xfs_agnumber_t		agno;	/* allocation group number */
	int			chunkidx; /* current index into inode chunk */
	int			clustidx; /* current index into inode cluster */
	xfs_btree_cur_t		*cur;	/* btree cursor for ialloc btree */
	int			end_of_ag; /* set if we've seen the ag end */
	int			error;	/* error code */
	int                     fmterror;/* bulkstat formatter result */
	int			i;	/* loop index */
	int			icount;	/* count of inodes good in irbuf */
206
	size_t			irbsize; /* size of irec buffer in bytes */
Linus Torvalds's avatar
Linus Torvalds committed
207
	xfs_ino_t		ino;	/* inode number (filesystem) */
208 209 210
	xfs_inobt_rec_incore_t	*irbp;	/* current irec buffer pointer */
	xfs_inobt_rec_incore_t	*irbuf;	/* start of irec buffer */
	xfs_inobt_rec_incore_t	*irbufend; /* end of good irec buffer entries */
211
	xfs_ino_t		lastino; /* last inode number returned */
212 213
	int			blks_per_cluster; /* # of blocks per cluster */
	int			inodes_per_cluster;/* # of inodes per cluster */
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216 217 218 219 220 221 222 223 224 225 226
	int			nirbuf;	/* size of irbuf */
	int			rval;	/* return value error code */
	int			tmp;	/* result value from btree calls */
	int			ubcount; /* size of user's buffer */
	int			ubleft;	/* bytes left in user's buffer */
	char			__user *ubufp;	/* pointer into user's buffer */
	int			ubelem;	/* spaces used in user's buffer */
	int			ubused;	/* bytes used by formatter */

	/*
	 * Get the last inode value, see if there's nothing to do.
	 */
	ino = (xfs_ino_t)*lastinop;
227
	lastino = ino;
Linus Torvalds's avatar
Linus Torvalds committed
228 229 230 231 232 233 234 235
	agno = XFS_INO_TO_AGNO(mp, ino);
	agino = XFS_INO_TO_AGINO(mp, ino);
	if (agno >= mp->m_sb.sb_agcount ||
	    ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
		*done = 1;
		*ubcountp = 0;
		return 0;
	}
236 237 238
	if (!ubcountp || *ubcountp <= 0) {
		return EINVAL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
239 240 241 242 243 244
	ubcount = *ubcountp; /* statstruct's */
	ubleft = ubcount * statstruct_size; /* bytes */
	*ubcountp = ubelem = 0;
	*done = 0;
	fmterror = 0;
	ubufp = ubuffer;
245 246
	blks_per_cluster = xfs_icluster_size_fsb(mp);
	inodes_per_cluster = blks_per_cluster << mp->m_sb.sb_inopblog;
247 248 249 250
	irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
	if (!irbuf)
		return ENOMEM;

251 252
	nirbuf = irbsize / sizeof(*irbuf);

Linus Torvalds's avatar
Linus Torvalds committed
253 254 255 256 257
	/*
	 * Loop over the allocation groups, starting from the last
	 * inode returned; 0 means start of the allocation group.
	 */
	rval = 0;
258 259
	while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
		cond_resched();
Linus Torvalds's avatar
Linus Torvalds committed
260 261 262 263 264 265 266 267 268 269 270 271 272
		error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
		if (error) {
			/*
			 * Skip this allocation group and go to the next one.
			 */
			agno++;
			agino = 0;
			continue;
		}
		agi = XFS_BUF_TO_AGI(agbp);
		/*
		 * Allocate and initialize a btree cursor for ialloc btree.
		 */
273 274
		cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno,
					    XFS_BTNUM_INO);
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278 279 280 281 282
		irbp = irbuf;
		irbufend = irbuf + nirbuf;
		end_of_ag = 0;
		/*
		 * If we're returning in the middle of an allocation group,
		 * we need to get the remainder of the chunk we're in.
		 */
		if (agino > 0) {
283 284
			xfs_inobt_rec_incore_t r;

Linus Torvalds's avatar
Linus Torvalds committed
285 286 287
			/*
			 * Lookup the inode chunk that this inode lives in.
			 */
288 289
			error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE,
						 &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
290 291 292
			if (!error &&	/* no I/O error */
			    tmp &&	/* lookup succeeded */
					/* got the record, should always work */
293
			    !(error = xfs_inobt_get_rec(cur, &r, &i)) &&
Linus Torvalds's avatar
Linus Torvalds committed
294 295
			    i == 1 &&
					/* this is the right chunk */
296
			    agino < r.ir_startino + XFS_INODES_PER_CHUNK &&
Linus Torvalds's avatar
Linus Torvalds committed
297
					/* lastino was not last in chunk */
298
			    (chunkidx = agino - r.ir_startino + 1) <
Linus Torvalds's avatar
Linus Torvalds committed
299 300
				    XFS_INODES_PER_CHUNK &&
					/* there are some left allocated */
301
			    xfs_inobt_maskn(chunkidx,
302 303
				    XFS_INODES_PER_CHUNK - chunkidx) &
				    ~r.ir_free) {
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306 307 308 309
				/*
				 * Grab the chunk record.  Mark all the
				 * uninteresting inodes (because they're
				 * before our start point) free.
				 */
				for (i = 0; i < chunkidx; i++) {
310 311
					if (XFS_INOBT_MASK(i) & ~r.ir_free)
						r.ir_freecount++;
Linus Torvalds's avatar
Linus Torvalds committed
312
				}
313 314 315 316
				r.ir_free |= xfs_inobt_maskn(0, chunkidx);
				irbp->ir_startino = r.ir_startino;
				irbp->ir_freecount = r.ir_freecount;
				irbp->ir_free = r.ir_free;
Linus Torvalds's avatar
Linus Torvalds committed
317
				irbp++;
318 319
				agino = r.ir_startino + XFS_INODES_PER_CHUNK;
				icount = XFS_INODES_PER_CHUNK - r.ir_freecount;
Linus Torvalds's avatar
Linus Torvalds committed
320 321 322 323 324 325 326 327 328 329 330 331
			} else {
				/*
				 * If any of those tests failed, bump the
				 * inode number (just in case).
				 */
				agino++;
				icount = 0;
			}
			/*
			 * In any case, increment to the next record.
			 */
			if (!error)
332
				error = xfs_btree_increment(cur, 0, &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
333 334 335 336
		} else {
			/*
			 * Start of ag.  Lookup the first inode chunk.
			 */
337
			error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
338 339 340 341 342 343 344
			icount = 0;
		}
		/*
		 * Loop through inode btree records in this ag,
		 * until we run out of inodes or space in the buffer.
		 */
		while (irbp < irbufend && icount < ubcount) {
345 346
			xfs_inobt_rec_incore_t r;

Linus Torvalds's avatar
Linus Torvalds committed
347 348 349 350 351 352 353
			/*
			 * Loop as long as we're unable to read the
			 * inode btree.
			 */
			while (error) {
				agino += XFS_INODES_PER_CHUNK;
				if (XFS_AGINO_TO_AGBNO(mp, agino) >=
354
						be32_to_cpu(agi->agi_length))
Linus Torvalds's avatar
Linus Torvalds committed
355
					break;
356 357
				error = xfs_inobt_lookup(cur, agino,
							 XFS_LOOKUP_GE, &tmp);
358
				cond_resched();
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362 363
			}
			/*
			 * If ran off the end of the ag either with an error,
			 * or the normal way, set end and stop collecting.
			 */
364 365 366 367 368 369 370
			if (error) {
				end_of_ag = 1;
				break;
			}

			error = xfs_inobt_get_rec(cur, &r, &i);
			if (error || i == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
371 372 373
				end_of_ag = 1;
				break;
			}
374

Linus Torvalds's avatar
Linus Torvalds committed
375 376
			/*
			 * If this chunk has any allocated inodes, save it.
377
			 * Also start read-ahead now for this chunk.
Linus Torvalds's avatar
Linus Torvalds committed
378
			 */
379
			if (r.ir_freecount < XFS_INODES_PER_CHUNK) {
380
				struct blk_plug	plug;
381 382 383 384 385
				/*
				 * Loop over all clusters in the next chunk.
				 * Do a readahead if there are any allocated
				 * inodes in that cluster.
				 */
386
				blk_start_plug(&plug);
387 388
				agbno = XFS_AGINO_TO_AGBNO(mp, r.ir_startino);
				for (chunkidx = 0;
389
				     chunkidx < XFS_INODES_PER_CHUNK;
390 391 392 393
				     chunkidx += inodes_per_cluster,
				     agbno += blks_per_cluster) {
					if (xfs_inobt_maskn(chunkidx,
					    inodes_per_cluster) & ~r.ir_free)
394
						xfs_btree_reada_bufs(mp, agno,
395
							agbno, blks_per_cluster,
396
							&xfs_inode_buf_ops);
397
				}
398
				blk_finish_plug(&plug);
399 400 401
				irbp->ir_startino = r.ir_startino;
				irbp->ir_freecount = r.ir_freecount;
				irbp->ir_free = r.ir_free;
Linus Torvalds's avatar
Linus Torvalds committed
402
				irbp++;
403
				icount += XFS_INODES_PER_CHUNK - r.ir_freecount;
Linus Torvalds's avatar
Linus Torvalds committed
404 405 406 407
			}
			/*
			 * Set agino to after this chunk and bump the cursor.
			 */
408
			agino = r.ir_startino + XFS_INODES_PER_CHUNK;
409
			error = xfs_btree_increment(cur, 0, &tmp);
410
			cond_resched();
Linus Torvalds's avatar
Linus Torvalds committed
411 412 413 414 415 416 417 418 419 420 421 422 423
		}
		/*
		 * Drop the btree buffers and the agi buffer.
		 * We can't hold any of the locks these represent
		 * when calling iget.
		 */
		xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
		xfs_buf_relse(agbp);
		/*
		 * Now format all the good inodes into the user's buffer.
		 */
		irbufend = irbp;
		for (irbp = irbuf;
424
		     irbp < irbufend && XFS_BULKSTAT_UBLEFT(ubleft); irbp++) {
Linus Torvalds's avatar
Linus Torvalds committed
425 426 427
			/*
			 * Now process this chunk of inodes.
			 */
428
			for (agino = irbp->ir_startino, chunkidx = clustidx = 0;
429
			     XFS_BULKSTAT_UBLEFT(ubleft) &&
430
				irbp->ir_freecount < XFS_INODES_PER_CHUNK;
Linus Torvalds's avatar
Linus Torvalds committed
431 432
			     chunkidx++, clustidx++, agino++) {
				ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
433

434
				ino = XFS_AGINO_TO_INO(mp, agno, agino);
Linus Torvalds's avatar
Linus Torvalds committed
435 436 437
				/*
				 * Skip if this inode is free.
				 */
438 439
				if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
					lastino = ino;
Linus Torvalds's avatar
Linus Torvalds committed
440
					continue;
441
				}
Linus Torvalds's avatar
Linus Torvalds committed
442 443 444 445
				/*
				 * Count used inodes as free so we can tell
				 * when the chunk is used up.
				 */
446
				irbp->ir_freecount++;
Linus Torvalds's avatar
Linus Torvalds committed
447 448 449 450 451

				/*
				 * Get the inode and fill in a single buffer.
				 */
				ubused = statstruct_size;
452
				error = formatter(mp, ino, ubufp, ubleft,
453
						  &ubused, &fmterror);
Linus Torvalds's avatar
Linus Torvalds committed
454
				if (fmterror == BULKSTAT_RV_NOTHING) {
455 456
					if (error && error != ENOENT &&
						error != EINVAL) {
457
						ubleft = 0;
458 459 460 461
						rval = error;
						break;
					}
					lastino = ino;
Linus Torvalds's avatar
Linus Torvalds committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475
					continue;
				}
				if (fmterror == BULKSTAT_RV_GIVEUP) {
					ubleft = 0;
					ASSERT(error);
					rval = error;
					break;
				}
				if (ubufp)
					ubufp += ubused;
				ubleft -= ubused;
				ubelem++;
				lastino = ino;
			}
476 477

			cond_resched();
Linus Torvalds's avatar
Linus Torvalds committed
478 479 480 481
		}
		/*
		 * Set up for the next loop iteration.
		 */
482
		if (XFS_BULKSTAT_UBLEFT(ubleft)) {
Linus Torvalds's avatar
Linus Torvalds committed
483 484 485
			if (end_of_ag) {
				agno++;
				agino = 0;
486 487
			} else
				agino = XFS_INO_TO_AGINO(mp, lastino);
Linus Torvalds's avatar
Linus Torvalds committed
488 489 490 491 492 493
		} else
			break;
	}
	/*
	 * Done, we're either out of filesystem or space to put the data.
	 */
494
	kmem_free(irbuf);
Linus Torvalds's avatar
Linus Torvalds committed
495
	*ubcountp = ubelem;
496 497 498 499 500
	/*
	 * Found some inodes, return them now and return the error next time.
	 */
	if (ubelem)
		rval = 0;
Linus Torvalds's avatar
Linus Torvalds committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	if (agno >= mp->m_sb.sb_agcount) {
		/*
		 * If we ran out of filesystem, mark lastino as off
		 * the end of the filesystem, so the next call
		 * will return immediately.
		 */
		*lastinop = (xfs_ino_t)XFS_AGINO_TO_INO(mp, agno, 0);
		*done = 1;
	} else
		*lastinop = (xfs_ino_t)lastino;

	return rval;
}

/*
 * Return stat information in bulk (by-inode) for the filesystem.
 * Special case for non-sequential one inode bulkstat.
 */
int					/* error status */
xfs_bulkstat_single(
	xfs_mount_t		*mp,	/* mount point for filesystem */
	xfs_ino_t		*lastinop, /* inode to return */
	char			__user *buffer, /* buffer with inode stats */
524
	int			*done)	/* 1 if there are more stats to get */
Linus Torvalds's avatar
Linus Torvalds committed
525 526 527 528 529 530 531 532
{
	int			count;	/* count value for bulkstat call */
	int			error;	/* return value */
	xfs_ino_t		ino;	/* filesystem inode number */
	int			res;	/* result from bs1 */

	/*
	 * note that requesting valid inode numbers which are not allocated
533
	 * to inodes will most likely cause xfs_imap_to_bp to generate warning
Linus Torvalds's avatar
Linus Torvalds committed
534 535 536 537 538 539
	 * messages about bad magic numbers. This is ok. The fact that
	 * the inode isn't actually an inode is handled by the
	 * error check below. Done this way to make the usual case faster
	 * at the expense of the error case.
	 */

540 541 542
	ino = *lastinop;
	error = xfs_bulkstat_one(mp, ino, buffer, sizeof(xfs_bstat_t),
				 NULL, &res);
Linus Torvalds's avatar
Linus Torvalds committed
543 544 545 546 547 548 549 550
	if (error) {
		/*
		 * Special case way failed, do it the "long" way
		 * to see if that works.
		 */
		(*lastinop)--;
		count = 1;
		if (xfs_bulkstat(mp, lastinop, &count, xfs_bulkstat_one,
551
				sizeof(xfs_bstat_t), buffer, done))
Linus Torvalds's avatar
Linus Torvalds committed
552 553 554 555 556 557 558 559 560 561 562
			return error;
		if (count == 0 || (xfs_ino_t)*lastinop != ino)
			return error == EFSCORRUPTED ?
				XFS_ERROR(EINVAL) : error;
		else
			return 0;
	}
	*done = 0;
	return 0;
}

563 564 565 566 567 568 569 570 571 572 573 574 575
int
xfs_inumbers_fmt(
	void			__user *ubuffer, /* buffer to write to */
	const xfs_inogrp_t	*buffer,	/* buffer to read from */
	long			count,		/* # of elements to read */
	long			*written)	/* # of bytes written */
{
	if (copy_to_user(ubuffer, buffer, count * sizeof(*buffer)))
		return -EFAULT;
	*written = count * sizeof(*buffer);
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
576 577 578 579 580 581 582 583
/*
 * Return inode number table for the filesystem.
 */
int					/* error status */
xfs_inumbers(
	xfs_mount_t	*mp,		/* mount point for filesystem */
	xfs_ino_t	*lastino,	/* last inode returned */
	int		*count,		/* size of buffer/count returned */
584 585
	void		__user *ubuffer,/* buffer with inode descriptions */
	inumbers_fmt_pf	formatter)
Linus Torvalds's avatar
Linus Torvalds committed
586 587 588 589 590 591 592 593 594
{
	xfs_buf_t	*agbp;
	xfs_agino_t	agino;
	xfs_agnumber_t	agno;
	int		bcount;
	xfs_inogrp_t	*buffer;
	int		bufidx;
	xfs_btree_cur_t	*cur;
	int		error;
595
	xfs_inobt_rec_incore_t r;
Linus Torvalds's avatar
Linus Torvalds committed
596 597 598 599 600 601 602 603 604 605
	int		i;
	xfs_ino_t	ino;
	int		left;
	int		tmp;

	ino = (xfs_ino_t)*lastino;
	agno = XFS_INO_TO_AGNO(mp, ino);
	agino = XFS_INO_TO_AGINO(mp, ino);
	left = *count;
	*count = 0;
606
	bcount = MIN(left, (int)(PAGE_SIZE / sizeof(*buffer)));
Linus Torvalds's avatar
Linus Torvalds committed
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
	buffer = kmem_alloc(bcount * sizeof(*buffer), KM_SLEEP);
	error = bufidx = 0;
	cur = NULL;
	agbp = NULL;
	while (left > 0 && agno < mp->m_sb.sb_agcount) {
		if (agbp == NULL) {
			error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
			if (error) {
				/*
				 * If we can't read the AGI of this ag,
				 * then just skip to the next one.
				 */
				ASSERT(cur == NULL);
				agbp = NULL;
				agno++;
				agino = 0;
				continue;
			}
625 626
			cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno,
						    XFS_BTNUM_INO);
627 628
			error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_GE,
						 &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
629 630 631 632 633 634
			if (error) {
				xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
				cur = NULL;
				xfs_buf_relse(agbp);
				agbp = NULL;
				/*
635
				 * Move up the last inode in the current
Linus Torvalds's avatar
Linus Torvalds committed
636 637 638 639 640 641 642
				 * chunk.  The lookup_ge will always get
				 * us the first inode in the next chunk.
				 */
				agino += XFS_INODES_PER_CHUNK - 1;
				continue;
			}
		}
643 644
		error = xfs_inobt_get_rec(cur, &r, &i);
		if (error || i == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
645 646 647 648 649 650 651 652
			xfs_buf_relse(agbp);
			agbp = NULL;
			xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
			cur = NULL;
			agno++;
			agino = 0;
			continue;
		}
653 654 655 656 657 658
		agino = r.ir_startino + XFS_INODES_PER_CHUNK - 1;
		buffer[bufidx].xi_startino =
			XFS_AGINO_TO_INO(mp, agno, r.ir_startino);
		buffer[bufidx].xi_alloccount =
			XFS_INODES_PER_CHUNK - r.ir_freecount;
		buffer[bufidx].xi_allocmask = ~r.ir_free;
Linus Torvalds's avatar
Linus Torvalds committed
659 660 661
		bufidx++;
		left--;
		if (bufidx == bcount) {
662 663
			long written;
			if (formatter(ubuffer, buffer, bufidx, &written)) {
Linus Torvalds's avatar
Linus Torvalds committed
664 665 666
				error = XFS_ERROR(EFAULT);
				break;
			}
667
			ubuffer += written;
Linus Torvalds's avatar
Linus Torvalds committed
668 669 670 671
			*count += bufidx;
			bufidx = 0;
		}
		if (left) {
672
			error = xfs_btree_increment(cur, 0, &tmp);
Linus Torvalds's avatar
Linus Torvalds committed
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
			if (error) {
				xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
				cur = NULL;
				xfs_buf_relse(agbp);
				agbp = NULL;
				/*
				 * The agino value has already been bumped.
				 * Just try to skip up to it.
				 */
				agino += XFS_INODES_PER_CHUNK;
				continue;
			}
		}
	}
	if (!error) {
		if (bufidx) {
689 690
			long written;
			if (formatter(ubuffer, buffer, bufidx, &written))
Linus Torvalds's avatar
Linus Torvalds committed
691 692 693 694 695 696
				error = XFS_ERROR(EFAULT);
			else
				*count += bufidx;
		}
		*lastino = XFS_AGINO_TO_INO(mp, agno, agino);
	}
697
	kmem_free(buffer);
Linus Torvalds's avatar
Linus Torvalds committed
698 699 700 701 702 703 704
	if (cur)
		xfs_btree_del_cursor(cur, (error ? XFS_BTREE_ERROR :
					   XFS_BTREE_NOERROR));
	if (agbp)
		xfs_buf_relse(agbp);
	return error;
}