Ping? > On Jan 21, 2016, at 6:02 PM, Eric Sandeen <sandeen@xxxxxxxxxxx> wrote: > > 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@xxxxxxxxxx> > --- > > > diff --git a/src/Makefile b/src/Makefile > index 4781736..48e6765 100644 > --- a/src/Makefile > +++ b/src/Makefile > @@ -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 = > > diff --git a/src/test-nextquota.c b/src/test-nextquota.c > new file mode 100644 > index 0000000..a2bbad9 > --- /dev/null > +++ b/src/test-nextquota.c > @@ -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; > +} > diff --git a/tests/generic/244 b/tests/generic/244 > new file mode 100755 > index 0000000..eb5a5ab > --- /dev/null > +++ b/tests/generic/244 > @@ -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 > diff --git a/tests/generic/244.out b/tests/generic/244.out > new file mode 100644 > index 0000000..df76947 > --- /dev/null > +++ b/tests/generic/244.out > @@ -0,0 +1,2 @@ > +QA output created by 244 > +Launch all quotas > diff --git a/tests/generic/group b/tests/generic/group > index 8ed383f..860ff4a 100644 > --- a/tests/generic/group > +++ b/tests/generic/group > @@ -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 > > > -- > To unsubscribe from this list: send the line "unsubscribe fstests" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe fstests" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html