Skip to content

Commit

Permalink
[XFS] 971064 Various fixups for xfs_bulkstat().
Browse files Browse the repository at this point in the history
- sanity check for NULL user buffer in xfs_ioc_bulkstat[_compat]()
- remove the special case for XFS_IOC_FSBULKSTAT with count == 1. This
  special case causes bulkstat to fail because the special case uses
  xfs_bulkstat_single() instead of xfs_bulkstat() and the two functions
  have different semantics.  xfs_bulkstat() will return the next inode
  after the one supplied while skipping internal inodes (ie quota inodes).
  xfs_bulkstate_single() will only lookup the inode supplied and return
  an error if it is an internal inode.
- in xfs_bulkstat(), need to initialise 'lastino' to the inode supplied
  so in cases were we return without examining any inodes the scan wont
  restart back at zero.
- sanity check for valid *ubcountp values. Cannot sanity check for valid
  ubuffer here because some users of xfs_bulkstat() don't supply a buffer.
- checks against 'ubleft' (the space left in the user's buffer) should be
  against 'statstruct_size' which is the supplied minimum object size.
  The mixture of checks against statstruct_size and 0 was one of the
  reasons we were skipping inodes.
- if the formatter function returns BULKSTAT_RV_NOTHING and an error and
  the error is not ENOENT or EINVAL then we need to abort the scan. ENOENT
  is for inodes that are no longer valid and we just skip them. EINVAL is
  returned if we try to lookup an internal inode so we skip them too. For
  a DMF scan if the inode and DMF attribute cannot fit into the space left
  in the user's buffer it would return ERANGE. We didn't handle this error
  and skipped the inode. We would continue to skip inodes until one fitted
  into the user's buffer or we completed the scan.
- put back the recalculation of agino (that got removed with the last fix)
  at the end of the while loop. This is because the code at the start of
  the loop expects agino to be the last inode examined if it is non-zero.
- if we found some inodes but then encountered an error, return success
  this time and the error next time. If the formatter aborted with ENOMEM
  we will now return this error but only if we couldn't read any inodes.
  Previously if we encountered ENOMEM without reading any inodes we
  returned a zero count and no error which falsely indicated the scan was
  complete.

SGI-PV: 973431
SGI-Modid: xfs-linux-melb:xfs-kern:30089a

Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: David Chinner <dgc@sgi.com>
  • Loading branch information
Lachlan McIlroy authored and Lachlan McIlroy committed Dec 10, 2007
1 parent d757762 commit cd57e59
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 26 deletions.
20 changes: 8 additions & 12 deletions fs/xfs/linux-2.6/xfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1047,24 +1047,20 @@ xfs_ioc_bulkstat(
if ((count = bulkreq.icount) <= 0)
return -XFS_ERROR(EINVAL);

if (bulkreq.ubuffer == NULL)
return -XFS_ERROR(EINVAL);

if (cmd == XFS_IOC_FSINUMBERS)
error = xfs_inumbers(mp, &inlast, &count,
bulkreq.ubuffer, xfs_inumbers_fmt);
else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
error = xfs_bulkstat_single(mp, &inlast,
bulkreq.ubuffer, &done);
else { /* XFS_IOC_FSBULKSTAT */
if (count == 1 && inlast != 0) {
inlast++;
error = xfs_bulkstat_single(mp, &inlast,
bulkreq.ubuffer, &done);
} else {
error = xfs_bulkstat(mp, &inlast, &count,
(bulkstat_one_pf)xfs_bulkstat_one, NULL,
sizeof(xfs_bstat_t), bulkreq.ubuffer,
BULKSTAT_FG_QUICK, &done);
}
}
else /* XFS_IOC_FSBULKSTAT */
error = xfs_bulkstat(mp, &inlast, &count,
(bulkstat_one_pf)xfs_bulkstat_one, NULL,
sizeof(xfs_bstat_t), bulkreq.ubuffer,
BULKSTAT_FG_QUICK, &done);

if (error)
return -error;
Expand Down
3 changes: 3 additions & 0 deletions fs/xfs/linux-2.6/xfs_ioctl32.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ xfs_ioc_bulkstat_compat(
if ((count = bulkreq.icount) <= 0)
return -XFS_ERROR(EINVAL);

if (bulkreq.ubuffer == NULL)
return -XFS_ERROR(EINVAL);

if (cmd == XFS_IOC_FSINUMBERS)
error = xfs_inumbers(mp, &inlast, &count,
bulkreq.ubuffer, xfs_inumbers_fmt_compat);
Expand Down
43 changes: 29 additions & 14 deletions fs/xfs/xfs_itable.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ xfs_bulkstat_use_dinode(
return 1;
}

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

/*
* Return stat information in bulk (by-inode) for the filesystem.
*/
Expand Down Expand Up @@ -353,7 +355,7 @@ xfs_bulkstat(
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 */
xfs_ino_t lastino=0; /* last inode number returned */
xfs_ino_t lastino; /* last inode number returned */
int nbcluster; /* # of blocks in a cluster */
int nicluster; /* # of inodes in a cluster */
int nimask; /* mask for inode clusters */
Expand All @@ -373,6 +375,7 @@ xfs_bulkstat(
* Get the last inode value, see if there's nothing to do.
*/
ino = (xfs_ino_t)*lastinop;
lastino = ino;
dip = NULL;
agno = XFS_INO_TO_AGNO(mp, ino);
agino = XFS_INO_TO_AGINO(mp, ino);
Expand All @@ -382,6 +385,9 @@ xfs_bulkstat(
*ubcountp = 0;
return 0;
}
if (!ubcountp || *ubcountp <= 0) {
return EINVAL;
}
ubcount = *ubcountp; /* statstruct's */
ubleft = ubcount * statstruct_size; /* bytes */
*ubcountp = ubelem = 0;
Expand All @@ -402,7 +408,8 @@ xfs_bulkstat(
* inode returned; 0 means start of the allocation group.
*/
rval = 0;
while (ubleft >= statstruct_size && agno < mp->m_sb.sb_agcount) {
while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
cond_resched();
bp = NULL;
down_read(&mp->m_peraglock);
error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
Expand Down Expand Up @@ -499,6 +506,7 @@ xfs_bulkstat(
break;
error = xfs_inobt_lookup_ge(cur, agino, 0, 0,
&tmp);
cond_resched();
}
/*
* If ran off the end of the ag either with an error,
Expand Down Expand Up @@ -542,6 +550,7 @@ xfs_bulkstat(
*/
agino = gino + XFS_INODES_PER_CHUNK;
error = xfs_inobt_increment(cur, 0, &tmp);
cond_resched();
}
/*
* Drop the btree buffers and the agi buffer.
Expand All @@ -555,12 +564,12 @@ xfs_bulkstat(
*/
irbufend = irbp;
for (irbp = irbuf;
irbp < irbufend && ubleft >= statstruct_size; irbp++) {
irbp < irbufend && XFS_BULKSTAT_UBLEFT(ubleft); irbp++) {
/*
* Now process this chunk of inodes.
*/
for (agino = irbp->ir_startino, chunkidx = clustidx = 0;
ubleft > 0 &&
XFS_BULKSTAT_UBLEFT(ubleft) &&
irbp->ir_freecount < XFS_INODES_PER_CHUNK;
chunkidx++, clustidx++, agino++) {
ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
Expand Down Expand Up @@ -663,15 +672,13 @@ xfs_bulkstat(
ubleft, private_data,
bno, &ubused, dip, &fmterror);
if (fmterror == BULKSTAT_RV_NOTHING) {
if (error == EFAULT) {
ubleft = 0;
rval = error;
break;
}
else if (error == ENOMEM)
if (error && error != ENOENT &&
error != EINVAL) {
ubleft = 0;
else
lastino = ino;
rval = error;
break;
}
lastino = ino;
continue;
}
if (fmterror == BULKSTAT_RV_GIVEUP) {
Expand All @@ -686,6 +693,8 @@ xfs_bulkstat(
ubelem++;
lastino = ino;
}

cond_resched();
}

if (bp)
Expand All @@ -694,11 +703,12 @@ xfs_bulkstat(
/*
* Set up for the next loop iteration.
*/
if (ubleft > 0) {
if (XFS_BULKSTAT_UBLEFT(ubleft)) {
if (end_of_ag) {
agno++;
agino = 0;
}
} else
agino = XFS_INO_TO_AGINO(mp, lastino);
} else
break;
}
Expand All @@ -707,6 +717,11 @@ xfs_bulkstat(
*/
kmem_free(irbuf, irbsize);
*ubcountp = ubelem;
/*
* Found some inodes, return them now and return the error next time.
*/
if (ubelem)
rval = 0;
if (agno >= mp->m_sb.sb_agcount) {
/*
* If we ran out of filesystem, mark lastino as off
Expand Down

0 comments on commit cd57e59

Please sign in to comment.