* 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 ------------------------------------------------------------------------------ _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel