Ignore that, sorry. I see Dave committed it. > On Feb 18, 2016, at 11:35 PM, Eric Sandeen <sandeen@xxxxxxxxxxx> wrote: > > 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 > -- 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