[PATCH 12/12] tests: Add tests for the quotactl syscall

Eugene Syromyatnikov evgsyr at gmail.com
Wed Sep 21 03:12:37 UTC 2016


* configure.ac (AC_CHECK_HEADERS): Add linux/dqblk_xfs.h, sys/quota.h.
* tests/.gitignore: Add quotactl, quotactl-xfs
* tests/Makefile.am (check_PROGRAMS): Likewise.
  (DECODER_TESTS): Add quotactl.test, quotactl-v.test,
  quotactl-xfs.test, quotactl-xfs-v.test.
  (EXTRA_DIST): quotactl.h
* quotactl-v.test: New file.
* quotactl-xfs-v.test: Likewise.
* quotactl-xfs.c: Likewise.
* quotactl-xfs.test: Likewise.
* quotactl.c: Likewise.
* quotactl.h: Likewise.
* quotactl.test: Likewise.
---
 configure.ac              |    3 +
 tests/.gitignore          |    2 +
 tests/Makefile.am         |    7 +
 tests/quotactl-v.test     |   10 ++
 tests/quotactl-xfs-v.test |   10 ++
 tests/quotactl-xfs.c      |  384 +++++++++++++++++++++++++++++++++++++++++++++
 tests/quotactl-xfs.test   |    6 +
 tests/quotactl.c          |  330 ++++++++++++++++++++++++++++++++++++++
 tests/quotactl.h          |  148 +++++++++++++++++
 tests/quotactl.test       |    6 +
 10 files changed, 906 insertions(+)
 create mode 100755 tests/quotactl-v.test
 create mode 100755 tests/quotactl-xfs-v.test
 create mode 100644 tests/quotactl-xfs.c
 create mode 100755 tests/quotactl-xfs.test
 create mode 100644 tests/quotactl.c
 create mode 100644 tests/quotactl.h
 create mode 100755 tests/quotactl.test

diff --git a/configure.ac b/configure.ac
index dc84a49..5c924e7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -353,6 +353,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	elf.h
 	inttypes.h
 	linux/bsg.h
+	linux/dqblk_xfs.h
 	linux/falloc.h
 	linux/fiemap.h
 	linux/filter.h
@@ -362,6 +363,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	linux/mmtimer.h
 	linux/msg.h
 	linux/perf_event.h
+	linux/quota.h
 	linux/seccomp.h
 	linux/securebits.h
 	linux/sem.h
@@ -378,6 +380,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	sys/fanotify.h
 	sys/ipc.h
 	sys/msg.h
+	sys/quota.h
 	sys/reg.h
 	sys/sem.h
 	sys/shm.h
diff --git a/tests/.gitignore b/tests/.gitignore
index 91fb111..bdb2d9a 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -186,6 +186,8 @@ prlimit64
 pselect6
 ptrace
 pwritev
+quotactl
+quotactl-xfs
 read-write
 readahead
 readdir
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 75d3171..ac579c7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -243,6 +243,8 @@ check_PROGRAMS = \
 	pselect6 \
 	ptrace \
 	pwritev \
+	quotactl \
+	quotactl-xfs \
 	read-write \
 	readahead \
 	readdir \
@@ -573,6 +575,10 @@ DECODER_TESTS = \
 	pselect6.test \
 	ptrace.test \
 	pwritev.test \
+	quotactl.test \
+	quotactl-v.test \
+	quotactl-xfs.test \
+	quotactl-xfs-v.test \
 	read-write.test \
 	readahead.test \
 	readdir.test \
@@ -753,6 +759,7 @@ EXTRA_DIST = init.sh run.sh match.awk \
 	     pipe.expected \
 	     ppoll.expected \
 	     ppoll-v.expected \
+	     quotactl.h \
 	     setfsugid.c \
 	     setreugid.c \
 	     setresugid.c \
diff --git a/tests/quotactl-v.test b/tests/quotactl-v.test
new file mode 100755
index 0000000..0eb5206
--- /dev/null
+++ b/tests/quotactl-v.test
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# Check non-abbreviated quotactl syscall decoding.
+
+. "${srcdir=.}/init.sh"
+
+NAME=quotactl
+PROG_ARGS="-v"
+
+run_strace_match_diff -v -a1
diff --git a/tests/quotactl-xfs-v.test b/tests/quotactl-xfs-v.test
new file mode 100755
index 0000000..0a35b1d
--- /dev/null
+++ b/tests/quotactl-xfs-v.test
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# Check non-abbreviated quotactl syscall decoding.
+
+. "${srcdir=.}/init.sh"
+
+NAME=quotactl-xfs
+PROG_ARGS="-v"
+
+run_strace_match_diff -v -a1 -e trace=quotactl
diff --git a/tests/quotactl-xfs.c b/tests/quotactl-xfs.c
new file mode 100644
index 0000000..bfbc83b
--- /dev/null
+++ b/tests/quotactl-xfs.c
@@ -0,0 +1,384 @@
+#include "tests.h"
+
+#include <asm/unistd.h>
+
+#if defined(__NR_quotactl) && \
+	(defined(HAVE_LINUX_QUOTA_H) || defined(HAVE_SYS_QUOTA_H)) && \
+	defined(HAVE_LINUX_DQBLK_XFS_H)
+
+# include <stdio.h>
+# include <string.h>
+# include <unistd.h>
+
+# include <linux/dqblk_xfs.h>
+
+# include "xlat.h"
+
+# include "quotactl.h"
+
+# if !defined(Q_GETNEXTQUOTA)
+#  define Q_XGETNEXTQUOTA XQM_CMD(0x9)
+# endif /* !defined(Q_GETNEXTQUOTA) */
+
+# ifndef Q_XGETQSTATV
+
+#  define Q_XGETQSTATV       XQM_CMD(8)
+#  define FS_QSTATV_VERSION1 1
+
+struct fs_qfilestatv {
+	__u64 qfs_ino;        /* inode number */
+	__u64 qfs_nblks;      /* number of BBs 512-byte-blks */
+	__u32 qfs_nextents;   /* number of extents */
+	__u32 qfs_pad;        /* pad for 8-byte alignment */
+};
+
+struct fs_quota_statv {
+	__s8                  qs_version;      /* version for future changes */
+	__u8                  qs_pad1;         /* pad for 16bit alignment */
+	__u16                 qs_flags;        /* XFS_QUOTA_.* flags */
+	__u32                 qs_incoredqs;    /* number of dquots incore */
+	struct fs_qfilestatv  qs_uquota;       /* user quota information */
+	struct fs_qfilestatv  qs_gquota;       /* group quota information */
+	struct fs_qfilestatv  qs_pquota;       /* project quota information */
+	__s32                 qs_btimelimit;   /* limit for blks timer */
+	__s32                 qs_itimelimit;   /* limit for inodes timer */
+	__s32                 qs_rtbtimelimit; /* limit for rt blks timer */
+	__u16                 qs_bwarnlimit;   /* limit for num warnings */
+	__u16                 qs_iwarnlimit;   /* limit for num warnings */
+	__u64                 qs_pad2[8];      /* for future proofing */
+};
+
+# endif /* !FS_QSTATV_VERSION1 */
+
+static struct xlat xfs_types[] = {
+	XLAT_PAIR(1 << 0, "XFS_USER_QUOTA"),
+	XLAT_PAIR(1 << 1, "XFS_PROJ_QUOTA"),
+	XLAT_PAIR(1 << 2, "XFS_GROUP_QUOTA"),
+	XLAT_END
+};
+
+static struct xlat xfs_flags[] = {
+	XLAT_PAIR(1 << 0, "XFS_QUOTA_UDQ_ACCT"),
+	XLAT_PAIR(1 << 1, "XFS_QUOTA_UDQ_ENFD"),
+	XLAT_PAIR(1 << 2, "XFS_QUOTA_GDQ_ACCT"),
+	XLAT_PAIR(1 << 3, "XFS_QUOTA_GDQ_ENFD"),
+	XLAT_PAIR(1 << 4, "XFS_QUOTA_PDQ_ACCT"),
+	XLAT_PAIR(1 << 5, "XFS_QUOTA_PDQ_ENFD"),
+	XLAT_END
+};
+
+
+static int verbose = 0;
+
+void
+print_xdisk_quota(int rc, void *ptr, void *arg)
+{
+	struct fs_disk_quota *dq = (struct fs_disk_quota *) ptr;
+	long out_arg = (long) arg;
+
+	if (((rc != 0) && out_arg) || (out_arg > 1)) {
+		printf("%p", dq);
+		return;
+	}
+
+	printf("{d_version=%" PRId8 ", ", dq->d_version);
+	printf("d_flags=");
+	printflags(xfs_types, (uint8_t)dq->d_flags, "XFS_???_QUOTA");
+
+	printf(", d_fieldmask=%#" PRIx16 ", "
+		"d_id=%" PRIu32 ", "
+		"d_blk_hardlimit=%" PRI__u64 ", "
+		"d_blk_softlimit=%" PRI__u64 ", "
+		"d_ino_hardlimit=%" PRI__u64 ", "
+		"d_ino_softlimit=%" PRI__u64 ", "
+		"d_bcount=%" PRI__u64 ", "
+		"d_icount=%" PRI__u64 ", ",
+		dq->d_fieldmask,
+		dq->d_id,
+		dq->d_blk_hardlimit,
+		dq->d_blk_softlimit,
+		dq->d_ino_hardlimit,
+		dq->d_ino_softlimit,
+		dq->d_bcount,
+		dq->d_icount);
+
+	if (!verbose) {
+		printf("...}");
+		return;
+	}
+
+	printf("d_itimer=%" PRId32 ", "
+		"d_btimer=%" PRId32 ", "
+		"d_iwarns=%" PRIu16 ", "
+		"d_bwarns=%" PRIu16 ", "
+		"d_rtb_hardlimit=%" PRI__u64 ", "
+		"d_rtb_softlimit=%" PRI__u64 ", "
+		"d_rtbcount=%" PRI__u64 ", "
+		"d_rtbtimer=%" PRId32 ", "
+		"d_rtbwarns=%" PRIu16 "}",
+		dq->d_itimer,
+		dq->d_btimer,
+		dq->d_iwarns,
+		dq->d_bwarns,
+		dq->d_rtb_hardlimit,
+		dq->d_rtb_softlimit,
+		dq->d_rtbcount,
+		dq->d_rtbtimer,
+		dq->d_rtbwarns);
+}
+
+void
+print_xquota_stat(int rc, void *ptr, void *arg)
+{
+	struct fs_quota_stat *qs = (struct fs_quota_stat *) ptr;
+	long out_arg = (long) arg;
+
+	if (((rc != 0) && out_arg) || (out_arg > 1)) {
+		printf("%p", qs);
+		return;
+	}
+
+	printf("{qs_version=%" PRId8 ", ", qs->qs_version);
+
+	if (!verbose) {
+		printf("...}");
+		return;
+	}
+
+	printf("qs_flags=");
+	printflags(xfs_flags, qs->qs_flags, "XFS_QUOTA_???");
+	printf(", qs_uquota={"
+			"qfs_ino=%" PRI__u64 ", "
+			"qfs_nblks=%" PRI__u64 ", "
+			"qfs_nextents=%" PRIu32 "}, "
+		"qs_gquota={"
+			"qfs_ino=%" PRI__u64 ", "
+			"qfs_nblks=%" PRI__u64 ", "
+			"qfs_nextents=%" PRIu32 "}, "
+		"qs_incoredqs=%" PRIu32 ", "
+		"qs_btimelimit=%" PRId32 ", "
+		"qs_itimelimit=%" PRId32 ", "
+		"qs_rtbtimelimit=%" PRId32 ", "
+		"qs_bwarnlimit=%" PRIu16 ", "
+		"qs_iwarnlimit=%" PRIu16 "}",
+		qs->qs_uquota.qfs_ino,
+		qs->qs_uquota.qfs_nblks,
+		qs->qs_uquota.qfs_nextents,
+		qs->qs_gquota.qfs_ino,
+		qs->qs_gquota.qfs_nblks,
+		qs->qs_gquota.qfs_nextents,
+		qs->qs_incoredqs,
+		qs->qs_btimelimit,
+		qs->qs_itimelimit,
+		qs->qs_rtbtimelimit,
+		qs->qs_bwarnlimit,
+		qs->qs_iwarnlimit);
+}
+
+void
+print_xquota_statv(int rc, void *ptr, void *arg)
+{
+	struct fs_quota_statv *qs = (struct fs_quota_statv *) ptr;
+	long out_arg = (long) arg;
+
+	if (((rc != 0) && out_arg) || (out_arg > 1)) {
+		printf("%p", qs);
+		return;
+	}
+
+	printf("{qs_version=%" PRId8 ", ", qs->qs_version);
+
+	if (!verbose) {
+		printf("...}");
+		return;
+	}
+
+	printf("qs_flags=");
+	printflags(xfs_flags, qs->qs_flags, "XFS_QUOTA_???");
+	printf(", qs_incoredqs=%" PRIu32 ", "
+		"qs_uquota={"
+			"qfs_ino=%" PRI__u64 ", "
+			"qfs_nblks=%" PRI__u64 ", "
+			"qfs_nextents=%" PRIu32 "}, "
+		"qs_gquota={"
+			"qfs_ino=%" PRI__u64 ", "
+			"qfs_nblks=%" PRI__u64 ", "
+			"qfs_nextents=%" PRIu32 "}, "
+		"qs_pquota={"
+			"qfs_ino=%" PRI__u64 ", "
+			"qfs_nblks=%" PRI__u64 ", "
+			"qfs_nextents=%" PRIu32 "}, "
+		"qs_btimelimit=%" PRId32 ", "
+		"qs_itimelimit=%" PRId32 ", "
+		"qs_rtbtimelimit=%" PRId32 ", "
+		"qs_bwarnlimit=%" PRIu16 ", "
+		"qs_iwarnlimit=%" PRIu16 "}",
+		qs->qs_incoredqs,
+		qs->qs_uquota.qfs_ino,
+		qs->qs_uquota.qfs_nblks,
+		qs->qs_uquota.qfs_nextents,
+		qs->qs_gquota.qfs_ino,
+		qs->qs_gquota.qfs_nblks,
+		qs->qs_gquota.qfs_nextents,
+		qs->qs_pquota.qfs_ino,
+		qs->qs_pquota.qfs_nblks,
+		qs->qs_pquota.qfs_nextents,
+		qs->qs_btimelimit,
+		qs->qs_itimelimit,
+		qs->qs_rtbtimelimit,
+		qs->qs_bwarnlimit,
+		qs->qs_iwarnlimit);
+}
+
+int
+main(int argc, char *argv[])
+{
+	char bogus_special_str[sizeof(void *) * 2 + sizeof("0x")];
+	char bogus_addr_str[sizeof(void *) * 2 + sizeof("0x")];
+	char unterminated_str[sizeof(void *) * 2 + sizeof("0x")];
+
+	long rc;
+	struct fs_disk_quota *xdq = tail_alloc(sizeof(*xdq));
+	struct fs_quota_stat *xqstat = tail_alloc(sizeof(*xqstat));
+	struct fs_quota_statv *xqstatv = tail_alloc(sizeof(*xqstatv));
+	uint32_t *flags = tail_alloc(sizeof(*flags));
+	char *unterminated = tail_memdup(unterminated_data,
+		sizeof(unterminated_data));
+
+	if ((argc > 1) && !strcmp(argv[1], "-v"))
+		verbose = 1;
+
+	snprintf(bogus_special_str, sizeof(bogus_special_str), "%p",
+		bogus_special);
+	snprintf(bogus_addr_str, sizeof(bogus_addr_str), "%p",
+		bogus_addr);
+	snprintf(unterminated_str, sizeof(unterminated_str), "%p",
+		unterminated);
+
+
+	/* Q_XQUOTAON */
+
+	*flags = 0xdeadbeef;
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_XQUOTAON, USRQUOTA)),
+		ARG_STR("/dev/bogus/"), flags, "[XFS_QUOTA_UDQ_ACCT|"
+		"XFS_QUOTA_UDQ_ENFD|XFS_QUOTA_GDQ_ACCT|XFS_QUOTA_GDQ_ENFD|"
+		"XFS_QUOTA_PDQ_ENFD|0xdeadbec0]");
+
+	rc = syscall(__NR_quotactl, QCMD(Q_XQUOTAON, 0xfacefeed), bogus_dev,
+		bogus_id, bogus_addr);
+	printf("quotactl(QCMD(Q_XQUOTAON, %#x /* ???QUOTA */), %s, "
+		"%p) = %s\n",
+		QCMD_TYPE(QCMD(Q_XQUOTAON, 0xfacefeed)),
+		bogus_dev_str, bogus_addr, sprintrc(rc));
+
+
+	/* Q_XQUOTAOFF */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_XQUOTAOFF, USRQUOTA)),
+		bogus_special, bogus_special_str,
+		bogus_addr, bogus_addr_str);
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_XQUOTAOFF, GRPQUOTA)),
+		ARG_STR("/dev/bogus/"),
+		ARG_STR(NULL));
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		QCMD(Q_XQUOTAOFF, 3), "QCMD(Q_XQUOTAOFF, 0x3 /* ???QUOTA */)",
+		ARG_STR("/dev/bogus/"), flags, "[XFS_QUOTA_UDQ_ACCT|"
+		"XFS_QUOTA_UDQ_ENFD|XFS_QUOTA_GDQ_ACCT|XFS_QUOTA_GDQ_ENFD|"
+		"XFS_QUOTA_PDQ_ENFD|0xdeadbec0]");
+
+
+	/* Q_XGETQUOTA */
+
+	/* Trying our best to get successful result */
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_GETQUOTA, USRQUOTA)),
+		ARG_STR("/dev/sda1"), getuid(), xdq, print_xdisk_quota,
+		(intptr_t)1);
+
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_GETQUOTA, GRPQUOTA)),
+		ARG_STR(NULL), -1, xdq, print_xdisk_quota, (intptr_t)2);
+
+
+	/* Q_XGETNEXTQUOTA */
+
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_XGETNEXTQUOTA, USRQUOTA)),
+		ARG_STR("/dev/sda1"), 0, xdq, print_xdisk_quota,
+		(intptr_t)1);
+
+
+	/* Q_XSETQLIM */
+
+	check_quota(CQF_NONE, ARG_STR(QCMD(Q_XSETQLIM, PRJQUOTA)),
+		bogus_special, bogus_special_str, 0, bogus_addr);
+
+	fill_memory_ex((char *)xdq, sizeof(*xdq), 0x8e);
+
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_XSETQLIM, PRJQUOTA)),
+		bogus_dev, bogus_dev_str, 3141592653U,
+		xdq, print_xdisk_quota, (intptr_t)0);
+
+
+	/* Q_XGETQSTAT */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_XGETQSTAT, USRQUOTA)),
+		ARG_STR("/dev/sda1"), xqstat, print_xquota_stat, (intptr_t)1);
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_XGETQSTATV, PRJQUOTA)),
+		unterminated, unterminated_str,
+		xqstat + 1, print_xquota_stat, (intptr_t)2);
+
+
+	/* Q_XGETQSTATV */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_XGETQSTAT, USRQUOTA)),
+		ARG_STR("/dev/sda1"), xqstatv, print_xquota_statv, 1);
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_XGETQSTATV, GRPQUOTA)),
+		ARG_STR(NULL), xqstatv, print_xquota_statv, (intptr_t)2);
+
+
+	/* Q_XQUOTARM */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_XQUOTARM, PRJQUOTA)),
+		bogus_special, bogus_special_str, ARG_STR(NULL));
+	check_quota(CQF_ID_SKIP,
+		ARG_STR(QCMD(Q_XQUOTARM, USRQUOTA)),
+		unterminated, unterminated_str, flags + 1);
+
+	*flags = 0xdeadbeef;
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_XQUOTARM, GRPQUOTA)),
+		ARG_STR(NULL), flags, "[XFS_USER_QUOTA|""XFS_PROJ_QUOTA|"
+		"XFS_GROUP_QUOTA|0xdeadbee8]");
+
+
+	/* Q_XQUOTASYNC */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		ARG_STR(QCMD(Q_XQUOTASYNC, USRQUOTA)),
+		bogus_special, bogus_special_str);
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		QCMD(Q_XQUOTASYNC, 0xfff),
+		"QCMD(Q_XQUOTASYNC, 0xff /* ???QUOTA */)",
+		ARG_STR(NULL));
+
+	puts("+++ exited with 0 +++");
+
+	return 0;
+}
+
+#else
+
+SKIP_MAIN_UNDEFINED("__NR_quotactl && "
+	"(HAVE_LINUX_QUOTA_H || HAVE_SYS_QUOTA_H) && "
+	"HAVE_LINUX_DQBLK_XFS_H");
+
+#endif
diff --git a/tests/quotactl-xfs.test b/tests/quotactl-xfs.test
new file mode 100755
index 0000000..b101a8c
--- /dev/null
+++ b/tests/quotactl-xfs.test
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Check quotactl syscall decoding.
+
+. "${srcdir=.}/init.sh"
+run_strace_match_diff -a1 -e trace=quotactl
diff --git a/tests/quotactl.c b/tests/quotactl.c
new file mode 100644
index 0000000..1efb15c
--- /dev/null
+++ b/tests/quotactl.c
@@ -0,0 +1,330 @@
+#include "tests.h"
+
+#include <asm/unistd.h>
+
+#if defined(__NR_quotactl) && \
+	(defined(HAVE_LINUX_QUOTA_H) || defined(HAVE_SYS_QUOTA_H))
+
+# include <inttypes.h>
+# include <stdint.h>
+# include <stdio.h>
+# include <string.h>
+# include <unistd.h>
+
+# include "xlat.h"
+# include "xlat/quota_formats.h"
+
+# include "quotactl.h"
+
+# if !defined(HAVE_LINUX_QUOTA_H)
+/* Some dirty hacks in order to make sys/quota.h usable as a backup */
+
+#  define if_dqblk dqblk
+#  define if_nextdqblk nextdqblk
+#  define if_dqinfo dqinfo
+
+# endif /* !defined(HAVE_LINUX_QUOTA_H) */
+
+# if !defined(Q_GETNEXTQUOTA)
+
+#  define Q_GETNEXTQUOTA 0x800009
+
+struct if_nextdqblk {
+	__u64 dqb_bhardlimit;
+	__u64 dqb_bsoftlimit;
+	__u64 dqb_curspace;
+	__u64 dqb_ihardlimit;
+	__u64 dqb_isoftlimit;
+	__u64 dqb_curinodes;
+	__u64 dqb_btime;
+	__u64 dqb_itime;
+	__u32 dqb_valid;
+	__u32 dqb_id;
+};
+# endif /* !defined(Q_GETNEXTQUOTA) */
+
+/* From documentation */
+static const struct xlat dqblk_valid_vals[] = {
+	XLAT_PAIR(1,  "QIF_BLIMITS"),
+	XLAT_PAIR(2,  "QIF_SPACE"),
+	XLAT_PAIR(4,  "QIF_ILIMITS"),
+	XLAT_PAIR(8,  "QIF_INODES"),
+	XLAT_PAIR(16, "QIF_BTIME"),
+	XLAT_PAIR(32, "QIF_ITIME"),
+	XLAT_END
+};
+
+static const struct xlat dqinfo_flags_vals[] = {
+	XLAT_PAIR(1 << 0,  "DQF_ROOT_SQUASH"),
+	XLAT_PAIR(1 << 16, "DQF_SYS_FILE"),
+	XLAT_END
+};
+
+static const struct xlat dqinfo_valid_vals[] = {
+	XLAT_PAIR(1, "IIF_BGRACE"),
+	XLAT_PAIR(2, "IIF_IGRACE"),
+	XLAT_PAIR(4, "IIF_FLAGS"),
+	XLAT_END
+};
+
+
+static int verbose = 0;
+
+void
+print_dqblk(long rc, void *db_void, void *arg)
+{
+	struct if_dqblk *db = (struct if_dqblk *) db_void;
+	long out_arg = (long) arg;
+
+	if (((rc != 0) && out_arg) || (out_arg > 1)) {
+		printf("%p", db);
+		return;
+	}
+
+	printf("{dqb_bhardlimit=%" PRI__u64 ", "
+		"dqb_bsoftlimit=%" PRI__u64 ", "
+		"dqb_curspace=%" PRI__u64 ", "
+		"dqb_ihardlimit=%" PRI__u64 ", "
+		"dqb_isoftlimit=%" PRI__u64 ", "
+		"dqb_curinodes=%" PRI__u64 ", ",
+		db->dqb_bhardlimit,
+		db->dqb_bsoftlimit,
+		db->dqb_curspace,
+		db->dqb_ihardlimit,
+		db->dqb_isoftlimit,
+		db->dqb_curinodes);
+
+	if (!verbose) {
+		printf("...}");
+		return;
+	}
+
+	printf("dqb_btime=%" PRI__u64 ", "
+		"dqb_itime=%" PRI__u64 ", "
+		"dqb_valid=",
+		db->dqb_btime,
+		db->dqb_itime);
+
+	printflags(dqblk_valid_vals, db->dqb_valid, "QIF_???");
+	printf("}");
+}
+
+void
+print_nextdqblk(long rc, void *db_void, void *arg)
+{
+	struct if_nextdqblk *db = (struct if_nextdqblk *) db_void;
+	long out_arg = (long) arg;
+
+	if (((rc != 0) && out_arg) || (out_arg > 1)) {
+		printf("%p", db);
+		return;
+	}
+
+	printf("{dqb_bhardlimit=%" PRI__u64 ", "
+		"dqb_bsoftlimit=%" PRI__u64 ", "
+		"dqb_curspace=%" PRI__u64 ", "
+		"dqb_ihardlimit=%" PRI__u64 ", "
+		"dqb_isoftlimit=%" PRI__u64 ", "
+		"dqb_curinodes=%" PRI__u64 ", ",
+		db->dqb_bhardlimit,
+		db->dqb_bsoftlimit,
+		db->dqb_curspace,
+		db->dqb_ihardlimit,
+		db->dqb_isoftlimit,
+		db->dqb_curinodes);
+
+	if (!verbose) {
+		printf("dqb_id=%u, ...}", db->dqb_id);
+		return;
+	}
+
+	printf("dqb_btime=%" PRI__u64 ", "
+		"dqb_itime=%" PRI__u64 ", "
+		"dqb_valid=",
+		db->dqb_btime,
+		db->dqb_itime);
+
+	printflags(dqblk_valid_vals, db->dqb_valid, "QIF_???");
+	printf("dqb_id=%u}", db->dqb_id);
+}
+
+void
+print_dqinfo(long rc, void *di_void, void *arg)
+{
+	struct if_dqinfo *di = (struct if_dqinfo *) di_void;
+	long out_arg = (long) arg;
+
+	if (((rc != 0) && out_arg) || (out_arg > 1)) {
+		printf("%p", di);
+		return;
+	}
+
+	printf("{dqi_bgrace=%" PRI__u64 ", "
+		"dqi_igrace=%" PRI__u64 ", "
+		"dqi_flags=",
+		di->dqi_bgrace, di->dqi_igrace);
+
+	printflags(dqinfo_flags_vals, di->dqi_flags, "DQF_???");
+	printf(", dqi_valid=");
+	printflags(dqinfo_valid_vals, di->dqi_valid, "IIF_???");
+	printf("}");
+}
+
+
+int
+main(int argc, char *argv[])
+{
+	char bogus_special_str[sizeof(void *) * 2 + sizeof("0x")];
+	char unterminated_str[sizeof(void *) * 2 + sizeof("0x")];
+
+	long rc;
+	char *unterminated = tail_memdup(unterminated_data,
+		sizeof(unterminated_data));
+	struct if_dqblk *dqblk = tail_alloc(sizeof(*dqblk));
+	struct if_dqinfo *dqinfo = tail_alloc(sizeof(*dqinfo));
+	uint32_t *fmt = tail_alloc(sizeof(*fmt));
+	struct if_nextdqblk *nextdqblk = tail_alloc(sizeof(*nextdqblk));
+
+
+	if ((argc > 1) && !strcmp(argv[1], "-v"))
+		verbose = 1;
+
+	snprintf(bogus_special_str, sizeof(bogus_special_str), "%p",
+		bogus_special);
+	snprintf(unterminated_str, sizeof(unterminated_str), "%p",
+		unterminated);
+
+
+	/* Invalid commands */
+
+	rc = syscall(__NR_quotactl, bogus_cmd, bogus_special, bogus_id,
+		bogus_addr);
+	printf("quotactl(QCMD(%#x /* Q_??? */, %#x /* ???QUOTA */), %p, %u, "
+		"%p) = %s\n",
+		QCMD_CMD(bogus_cmd), QCMD_TYPE(bogus_cmd), bogus_special,
+		bogus_id, bogus_addr, sprintrc(rc));
+
+	rc = syscall(__NR_quotactl, 0, NULL, -1, NULL);
+	printf("quotactl(QCMD(0 /* Q_??? */, USRQUOTA), NULL, -1, NULL) = %s\n",
+		sprintrc(rc));
+
+
+	/* Q_QUOTAON */
+
+	check_quota(CQF_ID_STR | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_QUOTAON, USRQUOTA)),
+		ARG_STR("/dev/bogus/"), ARG_STR(QFMT_VFS_OLD),
+		ARG_STR("/tmp/bogus/"));
+
+	rc = syscall(__NR_quotactl, QCMD(Q_QUOTAON, 0xfacefeed), bogus_dev,
+		bogus_id, bogus_addr);
+	printf("quotactl(QCMD(Q_QUOTAON, %#x /* ???QUOTA */), %s, "
+		"%#x /* QFMT_VFS_??? */, %p) = %s\n",
+		QCMD_TYPE(QCMD(Q_QUOTAON, 0xfacefeed)),
+		bogus_dev_str, bogus_id, bogus_addr, sprintrc(rc));
+
+
+	/* Q_QUOTAOFF */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		ARG_STR(QCMD(Q_QUOTAOFF, USRQUOTA)),
+		bogus_special, bogus_special_str);
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		ARG_STR(QCMD(Q_QUOTAOFF, GRPQUOTA)),
+		ARG_STR("/dev/bogus/"));
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		ARG_STR(QCMD(Q_QUOTAOFF, PRJQUOTA)), ARG_STR(NULL));
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		QCMD(Q_QUOTAOFF, 3), "QCMD(Q_QUOTAOFF, 0x3 /* ???QUOTA */)",
+		ARG_STR(NULL));
+
+
+	/* Q_GETQUOTA */
+
+	/* Trying our best to get successful result */
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_GETQUOTA, USRQUOTA)),
+		ARG_STR("/dev/sda1"), getuid(), dqblk, print_dqblk,
+		(intptr_t)1);
+
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_GETQUOTA, GRPQUOTA)),
+		ARG_STR(NULL), -1, dqblk, print_dqblk, (intptr_t)2);
+
+
+	/* Q_GETNEXTQUOTA */
+
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_GETNEXTQUOTA, USRQUOTA)),
+		ARG_STR("/dev/sda1"), 0, nextdqblk, print_nextdqblk,
+		(intptr_t)1);
+
+
+	/* Q_SETQUOTA */
+
+	fill_memory((char *)dqblk, sizeof(*dqblk));
+
+	check_quota(CQF_NONE, ARG_STR(QCMD(Q_SETQUOTA, PRJQUOTA)),
+		bogus_special, bogus_special_str, 0, bogus_addr);
+
+	check_quota(CQF_ADDR_CB, ARG_STR(QCMD(Q_SETQUOTA, PRJQUOTA)),
+		ARG_STR("/dev/bogus/"), 3141592653U, dqblk, print_dqblk,
+		(intptr_t)0);
+
+
+	/* Q_GETINFO */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_GETINFO, GRPQUOTA)),
+		ARG_STR("/dev/sda1"), dqinfo, print_dqinfo, (intptr_t)1);
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_GETINFO, GRPQUOTA)),
+		bogus_special, bogus_special_str, dqinfo,
+		print_dqinfo, (intptr_t)2);
+
+	/* Q_SETINFO */
+
+	fill_memory((char *)dqinfo, sizeof(*dqinfo));
+	/* In order to check flag printing correctness */
+	dqinfo->dqi_flags = 0xdeadabcd;
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_SETINFO, PRJQUOTA)),
+		bogus_special, bogus_special_str, ARG_STR(NULL));
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_CB,
+		ARG_STR(QCMD(Q_SETINFO, USRQUOTA)),
+		ARG_STR("/dev/bogus/"), dqinfo, print_dqinfo, (intptr_t)0);
+
+
+	/* Q_GETFMT */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_STR,
+		ARG_STR(QCMD(Q_GETFMT, PRJQUOTA)),
+		bogus_special, bogus_special_str, ARG_STR(NULL));
+	check_quota(CQF_ID_SKIP,
+		ARG_STR(QCMD(Q_GETFMT, USRQUOTA)),
+		unterminated, unterminated_str, fmt + 1);
+	check_quota(CQF_ID_SKIP,
+		ARG_STR(QCMD(Q_GETFMT, GRPQUOTA)),
+		ARG_STR("/dev/sda1"), fmt);
+
+
+	/* Q_SYNC */
+
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		ARG_STR(QCMD(Q_SYNC, USRQUOTA)),
+		bogus_special, bogus_special_str);
+	check_quota(CQF_ID_SKIP | CQF_ADDR_SKIP,
+		QCMD(Q_SYNC, 0xfff), "QCMD(Q_SYNC, 0xff /* ???QUOTA */)",
+		ARG_STR(NULL));
+
+	puts("+++ exited with 0 +++");
+
+	return 0;
+}
+
+#else
+
+SKIP_MAIN_UNDEFINED("__NR_quotactl && "
+	"(HAVE_LINUX_QUOTA_H || HAVE_SYS_QUOTA_H)");
+
+#endif
diff --git a/tests/quotactl.h b/tests/quotactl.h
new file mode 100644
index 0000000..a3ecb80
--- /dev/null
+++ b/tests/quotactl.h
@@ -0,0 +1,148 @@
+#ifndef STRACE_TESTS_QUOTACTL_H
+#define STRACE_TESTS_QUOTACTL_H
+
+/* Common definitions for Linux and XFS quota tests */
+
+# include <inttypes.h>
+# include <stdarg.h>
+# include <stdio.h>
+
+# ifdef HAVE_LINUX_QUOTA_H
+/* Broken in CentOS 5: has extern spinlock_t dq_data_lock; declaration */
+#  include <linux/quota.h>
+# else
+#  include <linux/types.h>
+/* Broken in some new glibc versions: have Q_GETNEXTQUOTA definition but no
+ * struct nextdqblk defined. Fixed in glibc-2.24-106-g4d72808. */
+#  include <sys/quota.h>
+# endif
+
+# ifndef QCMD_CMD
+#  define QCMD_CMD(_val) ((unsigned) (_val) >> SUBCMDSHIFT)
+# endif /* !QCMD_CMD */
+
+# ifndef QCMD_TYPE
+#  define QCMD_TYPE(_val) ((unsigned) (_val) & SUBCMDMASK)
+# endif /* !QCMD_TYPE */
+
+# ifndef PRJQUOTA
+#  define PRJQUOTA 2
+# endif
+
+# define ARG_STR(_arg) (_arg), #_arg
+
+typedef void (*print_cb)(long rc, void *addr, void *arg);
+
+enum check_quotactl_flag_bits {
+	CQF_ID_SKIP_BIT,
+	CQF_ID_STR_BIT,
+	CQF_ADDR_SKIP_BIT,
+	CQF_ADDR_STR_BIT,
+	CQF_ADDR_CB_BIT,
+};
+
+enum check_quotactl_flags {
+	CQF_NONE,
+	CQF_ID_SKIP   = 1 << CQF_ID_SKIP_BIT,
+	CQF_ID_STR    = 1 << CQF_ID_STR_BIT,
+	CQF_ADDR_SKIP = 1 << CQF_ADDR_SKIP_BIT,
+	CQF_ADDR_STR  = 1 << CQF_ADDR_STR_BIT,
+	CQF_ADDR_CB   = 1 << CQF_ADDR_CB_BIT,
+};
+
+
+static inline void
+fill_memory_ex(char *ptr, size_t size, unsigned char start)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++) {
+		ptr[i] = start + i % 80;
+	}
+}
+
+static inline void
+fill_memory(char *ptr, size_t size)
+{
+	fill_memory_ex(ptr, size, 0x80);
+}
+
+static inline void
+check_quota(uint32_t flags, int cmd, const char *cmd_str,
+	const char *special, const char *special_str, ...)
+{
+	long rc;
+	const char *addr_str = NULL;
+	const char *id_str = NULL;
+	void *addr = NULL;
+	print_cb addr_cb = NULL;
+	void *addr_cb_arg = NULL;
+	uint32_t id = -1;
+
+	va_list ap;
+
+	va_start(ap, special_str);
+
+	if (!(flags & CQF_ID_SKIP)) {
+		id = va_arg(ap, uint32_t);
+
+		if (flags & CQF_ID_STR)
+			id_str = va_arg(ap, const char *);
+	}
+
+	if (!(flags & CQF_ADDR_SKIP)) {
+		addr = va_arg(ap, void *);
+
+		if (flags & CQF_ADDR_CB) {
+			addr_cb = va_arg(ap, print_cb);
+			addr_cb_arg = va_arg(ap, void *);
+		} else if (flags & CQF_ADDR_STR) {
+			addr_str = va_arg(ap, const char *);
+		}
+	}
+
+	va_end(ap);
+
+	rc = syscall(__NR_quotactl, cmd, special, id, addr);
+	printf("quotactl(%s, %s", cmd_str, special_str);
+
+	if (!(flags & CQF_ID_SKIP)) {
+		if (flags & CQF_ID_STR) {
+			printf(", %s", id_str);
+		} else {
+			if (id == (uint32_t)-1)
+				printf(", -1");
+			else
+				printf(", %u", id);
+		}
+	}
+
+	if (!(flags & CQF_ADDR_SKIP)) {
+		if (flags & CQF_ADDR_CB) {
+			printf(", ");
+			addr_cb(rc, addr, addr_cb_arg);
+		} else if (flags & CQF_ADDR_STR) {
+			printf(", %s", addr_str);
+		} else {
+			printf(", %p", addr);
+		}
+	}
+
+	printf(") = %s\n", sprintrc(rc));
+}
+
+
+static const int bogus_cmd = 0xbadc0ded;
+static const char * const bogus_special =
+	(const char *) (unsigned long) 0xfffffca7ffffc0deULL;
+static const int bogus_id = 0xca7faced;
+static void * const bogus_addr =
+	(void *) (unsigned long) 0xffffda7affffdeadULL;
+
+/* It is invalid anyway due to the flash in the end */
+static const char *bogus_dev = "/dev/bogus/";
+static const char *bogus_dev_str = "\"/dev/bogus/\"";
+
+static const char unterminated_data[] = { '\1', '\2', '\3' };
+
+#endif /* !STRACE_TESTS_QUOTACTL_H */
diff --git a/tests/quotactl.test b/tests/quotactl.test
new file mode 100755
index 0000000..4ebf7c7
--- /dev/null
+++ b/tests/quotactl.test
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Check quotactl syscall decoding.
+
+. "${srcdir=.}/init.sh"
+run_strace_match_diff -a1
-- 
1.7.10.4





More information about the Strace-devel mailing list