quota: test Q_GETNEXTQUOTA

The new Q_GETNEXTQUOTA quotactl (not yet merged) is designed
to take an ID as input ala Q_GETQUOTA, and return the quota
for the next active ID >= the input ID.  This lets us quickly
iterate over all existing quotas by leveraging the kernel's
knowledge of which quotas are allocated and active.

The test contains a new helper binary, test-nextquota, which
tests both the "vfs" and "xfs" versions of the quotactl.
It accepts an ID, and outputs the returned ID, ihard, and
isoft values for that quota.  It doesn't return block information
simply because that can vary depending on fs, block size, etc,
and we want something very consistent as output, for verifiation.

The test harness sets quotas for 100 random IDs, remounts,
and uses these quotactls to iterate over all the IDs we set,
using the test binary, making sure we get back what we expect.

Not the prettiest thing, but it works!

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Eric Sandeen
2016-02-08 09:27:14 +11:00
committed by Dave Chinner
parent 234f51ebbd
commit 1dfb50585c
5 changed files with 298 additions and 1 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
bulkstat_unlink_test_modified t_dir_offset t_futimens t_immutable \
stale_handle pwrite_mmap_blocked t_dir_offset2 seek_sanity_test \
seek_copy_test t_readdir_1 t_readdir_2 fsync-tester nsexec cloner \
renameat2 t_getcwd e4compact
renameat2 t_getcwd e4compact test-nextquota
SUBDIRS =
+163
View File
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2016 Red Hat, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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.
*
* 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
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/quota.h>
#include <sys/types.h>
#include <xfs/xqm.h>
/*
* Exercise the Q_GETNEXTQUOTA and Q_XGETNEXTQUOTA quotactls.
* Really only returns a bare minimum of quota information,
* just enough to be sure we got a sane answer back.
*
* These quotactls take a quota ID as input, and return the
* next active quota >= that ID.
*
* usage:
* test-nextquota [-v] -[u|g|p] -i id -d device
*/
#ifndef PRJQUOTA
#define PRJQUOTA 2
#endif
#ifndef Q_GETNEXTQUOTA
#define Q_GETNEXTQUOTA 0x800009 /* get disk limits and usage >= ID */
struct nextdqblk
{
u_int64_t dqb_bhardlimit; /* absolute limit on disk quota blocks alloc */
u_int64_t dqb_bsoftlimit; /* preferred limit on disk quota blocks */
u_int64_t dqb_curspace; /* current quota block count */
u_int64_t dqb_ihardlimit; /* maximum # allocated inodes */
u_int64_t dqb_isoftlimit; /* preferred inode limit */
u_int64_t dqb_curinodes; /* current # allocated inodes */
u_int64_t dqb_btime; /* time limit for excessive disk use */
u_int64_t dqb_itime; /* time limit for excessive files */
u_int32_t dqb_valid; /* bitmask of QIF_* constants */
u_int32_t dqb_id; /* id for this quota info*/
};
#endif
#ifndef Q_XGETNEXTQUOTA
#define Q_XGETNEXTQUOTA XQM_CMD(9)
#endif
void usage(char *progname)
{
printf("usage: %s [-v] -[u|g|p] -i id -d device\n", progname);
exit(1);
}
int main(int argc, char *argv[])
{
int c;
int cmd;
int type = -1, typeflag = 0;
int verbose = 0;
uint id = 0, idflag = 0;
char *device = NULL;
struct nextdqblk dqb;
struct fs_disk_quota xqb;
while ((c = getopt(argc,argv,"ugpi:d:v")) != EOF) {
switch (c) {
case 'u':
type = USRQUOTA;
typeflag++;
break;
case 'g':
type = GRPQUOTA;
typeflag++;
break;
case 'p':
type = PRJQUOTA;
typeflag++;
break;
case 'i':
id = atoi(optarg);
idflag++;
break;
case 'd':
device = optarg;
break;
case 'v':
verbose++;
break;
default:
usage(argv[0]);
}
}
if (idflag == 0) {
printf("No id specified\n");
usage(argv[0]);
}
if (typeflag == 0) {
printf("No type specified\n");
usage(argv[0]);
}
if (typeflag > 1) {
printf("Multiple types specified\n");
usage(argv[0]);
}
if (device == NULL) {
printf("No device specified\n");
usage(argv[0]);
}
if (verbose)
printf("asking for quota type %d for id %u on %s\n", type, id, device);
memset(&dqb, 0, sizeof(struct nextdqblk));
memset(&xqb, 0, sizeof(struct fs_disk_quota));
if (verbose)
printf("====Q_GETNEXTQUOTA====\n");
cmd = QCMD(Q_GETNEXTQUOTA, type);
if (quotactl(cmd, device, id, (void *)&dqb) < 0) {
perror("Q_GETNEXTQUOTA");
return 1;
}
/*
* We only print id and inode limits because
* block count varies depending on fs block size, etc;
* this is just a sanity test that we can retrieve the quota,
* and inode limits have the same units across both calls.
*/
printf("id %u\n", dqb.dqb_id);
printf("ihard %llu\n", (unsigned long long)dqb.dqb_ihardlimit);
printf("isoft %llu\n", (unsigned long long)dqb.dqb_isoftlimit);
if (verbose)
printf("====Q_XGETNEXTQUOTA====\n");
cmd = QCMD(Q_XGETNEXTQUOTA, USRQUOTA);
if (quotactl(cmd, device, id, (void *)&xqb) < 0) {
perror("Q_XGETNEXTQUOTA");
return 1;
}
printf("id %u\n", xqb.d_id);
printf("ihard %llu\n", xqb.d_ino_hardlimit);
printf("isoft %llu\n", xqb.d_ino_softlimit);
return 0;
}
+131
View File
@@ -0,0 +1,131 @@
#! /bin/bash
# FS QA Test 244
#
# test out "sparse" quota ids retrieved by Q_GETNEXTQUOTA
#
# Designed to use the new Q_GETNEXTQUOTA quotactl
#
#-----------------------------------------------------------------------
# Copyright (c) 2016 Red Hat, Inc. All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
#
# 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.
#
# 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
#-----------------------------------------------------------------------
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15
_cleanup()
{
cat $tmp.IDs >> $seqres.full
cd /
rm -f $tmp.*
}
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
. ./common/quota
# remove previous $seqres.full before test
rm -f $seqres.full
# real QA test starts here
_supported_fs generic
_supported_os Linux
_require_quota
_require_scratch
scratch_unmount 2>/dev/null
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount "-o usrquota,grpquota"
quotacheck -u -g $SCRATCH_MNT 2>/dev/null
quotaon $SCRATCH_MNT 2>/dev/null
_scratch_unmount
TYPES="u g"
MOUNT_OPTIONS="-o usrquota,grpquota"
_qmount
quotaon $SCRATCH_MNT 2>/dev/null
# Ok, do we even have GETNEXTQUOTA? Querying ID 0 should work.
$here/src/test-nextquota -i 0 -u -d $SCRATCH_DEV &> $seqres.full || \
_notrun "No GETNEXTQUOTA support"
echo "Launch all quotas"
# Ideally we'd carefully test edge conditions of "sparse"
# quota ids at beginnings and ends of otherwise empty disk
# blocks, etc, but that's pretty fs-specific.
# So just spray a bunch of random IDs into quota, and make
# sure we get them all back.
ITERATIONS=100
# A few extra on the off chance we get dups
for I in `seq 1 $(($ITERATIONS+10))`; do
ID=`od -N 4 -t uL -An /dev/urandom | tr -d " "`
echo $ID >> $tmp.1
done
# sort & uniq to remove dups & facilitate reading them back
# On the off chance we got ID 0, remove it.
sort -n $tmp.1 | uniq | head -n ${ITERATIONS} | grep -vw 0 > $tmp.IDs
# Populate a bunch of random quotas on the filesystem:
for TYPE in u g; do
for ID in `cat $tmp.IDs`; do
setquota -${TYPE} $ID $ID $ID $ID $ID $SCRATCH_MNT
touch ${SCRATCH_MNT}/${ID}
chown ${ID} ${SCRATCH_MNT}/${ID}
done
done
# remount just for kicks, make sure we get it off disk
_scratch_unmount
_qmount
quotaon $SCRATCH_MNT 2>/dev/null
# Read them back by iterating based on quotas returned.
# This should match what we set, even if we don't directly
# ask for each exact id, but just ask for "next" id after
# each one we got back last.
for TYPE in u g; do
# root is always there but not in our random IDs; start at 1
NEXT=1
for ID in `cat $tmp.IDs`; do
echo "Trying ID $NEXT expecting $ID" >> $seqres.full
Q=`$here/src/test-nextquota -i $NEXT -${TYPE} -d $SCRATCH_DEV` \
|| _fail "test-nextquota failed: $Q"
echo $Q >> $seqres.full
# ID and its inode limits should match
echo "$Q" | grep -qw ${ID} || _fail "Didn't get id $ID"
# Get the ID returned from the test
NEXT=`echo "$Q" | grep ^id | awk '{print $NF}' | head -n 1`
# Advance that ID by one, and ask for another search
let NEXT=NEXT+1
done
done
# success, all done
status=0
exit
+2
View File
@@ -0,0 +1,2 @@
QA output created by 244
Launch all quotas
+1
View File
@@ -246,6 +246,7 @@
241 auto
242 auto quick clone
243 auto quick clone
244 auto quick quota
245 auto quick dir
246 auto quick rw
247 auto quick rw