From 81b63834dd13d12676a644e8359a6016a3545c37 Mon Sep 17 00:00:00 2001 From: Eugene Syromyatnikov Date: Wed, 21 Sep 2016 06:12:37 +0300 Subject: [PATCH] tests: check decoding of quotactl syscall * configure.ac (AC_CHECK_HEADERS): Add linux/dqblk_xfs.h, linux/quota.h, and sys/quota.h. * tests/.gitignore: Add quotactl, quotactl-v, quotactl-xfs, and quotactl-xfs-v. * tests/Makefile.am (check_PROGRAMS): Likewise. (DECODER_TESTS): Add quotactl.test, quotactl-v.test, quotactl-xfs.test, and quotactl-xfs-v.test. (EXTRA_DIST): Add quotactl.h * quotactl.h: New file. * quotactl.c: Likewise. * quotactl-v.c: Likewise. * quotactl-xfs.c: Likewise. * quotactl-xfs-v.c: Likewise. * quotactl.test: New test. * quotactl-v.test: Likewise. * quotactl-xfs.test: Likewise. * quotactl-xfs-v.test: Likewise. --- configure.ac | 3 + tests/.gitignore | 4 + tests/Makefile.am | 9 + tests/quotactl-v.c | 3 + tests/quotactl-v.test | 6 + tests/quotactl-xfs-v.c | 3 + tests/quotactl-xfs-v.test | 6 + tests/quotactl-xfs.c | 351 ++++++++++++++++++++++++++++++++++++++ tests/quotactl-xfs.test | 6 + tests/quotactl.c | 317 ++++++++++++++++++++++++++++++++++ tests/quotactl.h | 188 ++++++++++++++++++++ tests/quotactl.test | 6 + 12 files changed, 902 insertions(+) create mode 100644 tests/quotactl-v.c create mode 100755 tests/quotactl-v.test create mode 100644 tests/quotactl-xfs-v.c 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 dc84a492..5c924e7e 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 84a795d0..833799a8 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -185,6 +185,10 @@ prlimit64 pselect6 ptrace pwritev +quotactl +quotactl-v +quotactl-xfs +quotactl-xfs-v read-write readahead readdir diff --git a/tests/Makefile.am b/tests/Makefile.am index 3e3d073e..5a69a553 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -242,6 +242,10 @@ check_PROGRAMS = \ pselect6 \ ptrace \ pwritev \ + quotactl \ + quotactl-v \ + quotactl-xfs \ + quotactl-xfs-v \ read-write \ readahead \ readdir \ @@ -571,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 \ @@ -751,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.c b/tests/quotactl-v.c new file mode 100644 index 00000000..00139649 --- /dev/null +++ b/tests/quotactl-v.c @@ -0,0 +1,3 @@ +/* This file is part of quotactl-v strace test. */ +#define VERBOSE 1 +#include "quotactl.c" diff --git a/tests/quotactl-v.test b/tests/quotactl-v.test new file mode 100755 index 00000000..6adaf82c --- /dev/null +++ b/tests/quotactl-v.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check non-abbreviated decoding of quotactl syscall. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -v -e trace=quotactl diff --git a/tests/quotactl-xfs-v.c b/tests/quotactl-xfs-v.c new file mode 100644 index 00000000..21173d63 --- /dev/null +++ b/tests/quotactl-xfs-v.c @@ -0,0 +1,3 @@ +/* This file is part of quotactl-xfs-v strace test. */ +#define VERBOSE 1 +#include "quotactl-xfs.c" diff --git a/tests/quotactl-xfs-v.test b/tests/quotactl-xfs-v.test new file mode 100755 index 00000000..d5ffc7d6 --- /dev/null +++ b/tests/quotactl-xfs-v.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check non-abbreviated decoding of quotactl xfs subcommands. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -v -e trace=quotactl diff --git a/tests/quotactl-xfs.c b/tests/quotactl-xfs.c new file mode 100644 index 00000000..a08ccd25 --- /dev/null +++ b/tests/quotactl-xfs.c @@ -0,0 +1,351 @@ +/* + * Check decoding of quotactl xfs subcommands. + * + * Copyright (c) 2016 Eugene Syromiatnikov + * Copyright (c) 2016 Dmitry V. Levin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tests.h" + +#include + +#if defined(__NR_quotactl) && \ + (defined(HAVE_LINUX_QUOTA_H) || defined(HAVE_SYS_QUOTA_H)) && \ + defined(HAVE_LINUX_DQBLK_XFS_H) + +# include +# include +# include + +# include + +# include "quotactl.h" + +# ifndef Q_GETNEXTQUOTA +# define Q_XGETNEXTQUOTA XQM_CMD(0x9) +# endif /* !Q_GETNEXTQUOTA */ + +# ifndef Q_XGETQSTATV + +# define Q_XGETQSTATV XQM_CMD(8) +# define FS_QSTATV_VERSION1 1 + +struct fs_qfilestatv { + uint64_t qfs_ino; /* inode number */ + uint64_t qfs_nblks; /* number of BBs 512-byte-blks */ + uint32_t qfs_nextents; /* number of extents */ + uint32_t qfs_pad; /* pad for 8-byte alignment */ +}; + +struct fs_quota_statv { + int8_t qs_version; /* version for future changes */ + uint8_t qs_pad1; /* pad for 16bit alignment */ + uint16_t qs_flags; /* XFS_QUOTA_.* flags */ + uint32_t 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 */ + int32_t qs_btimelimit; /* limit for blks timer */ + int32_t qs_itimelimit; /* limit for inodes timer */ + int32_t qs_rtbtimelimit; /* limit for rt blks timer */ + uint16_t qs_bwarnlimit; /* limit for num warnings */ + uint16_t qs_iwarnlimit; /* limit for num warnings */ + uint64_t qs_pad2[8]; /* for future proofing */ +}; + +# endif /* !Q_XGETQSTATV */ + +# include "xlat.h" +# include "xlat/xfs_dqblk_flags.h" +# if VERBOSE +# include "xlat/xfs_quota_flags.h" +# endif + + +void +print_xdisk_quota(int rc, void *ptr, void *arg) +{ + struct fs_disk_quota *dq = ptr; + long out_arg = (long) arg; + + if (((rc != 0) && out_arg) || (out_arg > 1)) { + printf("%p", dq); + return; + } + + PRINT_FIELD_D("{", dq, d_version); + printf(", d_flags="); + printflags(xfs_dqblk_flags, (uint8_t) dq->d_flags, "XFS_???_QUOTA"); + + PRINT_FIELD_X(", ", dq, d_fieldmask); + PRINT_FIELD_U(", ", dq, d_id); + PRINT_FIELD_U(", ", dq, d_blk_hardlimit); + PRINT_FIELD_U(", ", dq, d_blk_softlimit); + PRINT_FIELD_U(", ", dq, d_ino_hardlimit); + PRINT_FIELD_U(", ", dq, d_ino_softlimit); + PRINT_FIELD_U(", ", dq, d_bcount); + PRINT_FIELD_U(", ", dq, d_icount); + +# if VERBOSE + PRINT_FIELD_D(", ", dq, d_itimer); + PRINT_FIELD_D(", ", dq, d_btimer); + PRINT_FIELD_U(", ", dq, d_iwarns); + PRINT_FIELD_U(", ", dq, d_bwarns); + PRINT_FIELD_U(", ", dq, d_rtb_hardlimit); + PRINT_FIELD_U(", ", dq, d_rtb_softlimit); + PRINT_FIELD_U(", ", dq, d_rtbcount); + PRINT_FIELD_D(", ", dq, d_rtbtimer); + PRINT_FIELD_U(", ", dq, d_rtbwarns); +# else + printf(", ..."); +# endif /* !VERBOSE */ + printf("}"); +} + +void +print_xquota_stat(int rc, void *ptr, void *arg) +{ + struct fs_quota_stat *qs = ptr; + long out_arg = (long) arg; + + if (((rc != 0) && out_arg) || (out_arg > 1)) { + printf("%p", qs); + return; + } + + PRINT_FIELD_D("{", qs, qs_version); + +# if VERBOSE + printf(", qs_flags="); + printflags(xfs_quota_flags, qs->qs_flags, "XFS_QUOTA_???"); + PRINT_FIELD_U(", qs_uquota={", &qs->qs_uquota, qfs_ino); + PRINT_FIELD_U(", ", &qs->qs_uquota, qfs_nblks); + PRINT_FIELD_U(", ", &qs->qs_uquota, qfs_nextents); + PRINT_FIELD_U("}, qs_gquota={", &qs->qs_gquota, qfs_ino); + PRINT_FIELD_U(", ", &qs->qs_gquota, qfs_nblks); + PRINT_FIELD_U(", ", &qs->qs_gquota, qfs_nextents); + PRINT_FIELD_U("}, ", qs, qs_incoredqs); + PRINT_FIELD_D(", ", qs, qs_btimelimit); + PRINT_FIELD_D(", ", qs, qs_itimelimit); + PRINT_FIELD_D(", ", qs, qs_rtbtimelimit); + PRINT_FIELD_U(", ", qs, qs_bwarnlimit); + PRINT_FIELD_U(", ", qs, qs_iwarnlimit); +# else + printf(", ..."); +# endif /* !VERBOSE */ + printf("}"); +} + +void +print_xquota_statv(int rc, void *ptr, void *arg) +{ + struct fs_quota_statv *qs = ptr; + long out_arg = (long) arg; + + if (((rc != 0) && out_arg) || (out_arg > 1)) { + printf("%p", qs); + return; + } + + PRINT_FIELD_D("{", qs, qs_version); + +# if VERBOSE + printf(", qs_flags="); + printflags(xfs_quota_flags, qs->qs_flags, "XFS_QUOTA_???"); + PRINT_FIELD_U(", ", qs, qs_incoredqs); + PRINT_FIELD_U(", qs_uquota={", &qs->qs_uquota, qfs_ino); + PRINT_FIELD_U(", ", &qs->qs_uquota, qfs_nblks); + PRINT_FIELD_U(", ", &qs->qs_uquota, qfs_nextents); + PRINT_FIELD_U("}, qs_gquota={", &qs->qs_gquota, qfs_ino); + PRINT_FIELD_U(", ", &qs->qs_gquota, qfs_nblks); + PRINT_FIELD_U(", ", &qs->qs_gquota, qfs_nextents); + PRINT_FIELD_U("}, qs_pquota={", &qs->qs_pquota, qfs_ino); + PRINT_FIELD_U(", ", &qs->qs_pquota, qfs_nblks); + PRINT_FIELD_U(", ", &qs->qs_pquota, qfs_nextents); + PRINT_FIELD_D("}, ", qs, qs_btimelimit); + PRINT_FIELD_D(", ", qs, qs_itimelimit); + PRINT_FIELD_D(", ", qs, qs_rtbtimelimit); + PRINT_FIELD_U(", ", qs, qs_bwarnlimit); + PRINT_FIELD_U(", ", qs, qs_iwarnlimit); +# else + printf(", ..."); +# endif /* !VERBOSE */ + printf("}"); +} + +int +main(void) +{ + 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)); + + 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 00000000..404a7372 --- /dev/null +++ b/tests/quotactl-xfs.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check decoding of quotactl xfs subcommands. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -e trace=quotactl diff --git a/tests/quotactl.c b/tests/quotactl.c new file mode 100644 index 00000000..d68c4ec8 --- /dev/null +++ b/tests/quotactl.c @@ -0,0 +1,317 @@ +/* + * Check decoding of quotactl syscall. + * + * Copyright (c) 2016 Eugene Syromiatnikov + * Copyright (c) 2016 Dmitry V. Levin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tests.h" + +#include + +#if defined(__NR_quotactl) && \ + (defined(HAVE_LINUX_QUOTA_H) || defined(HAVE_SYS_QUOTA_H)) + +# include +# include +# include +# include +# include + +# include "quotactl.h" + +# ifndef 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 /* !HAVE_LINUX_QUOTA_H */ + +# ifndef Q_GETNEXTQUOTA + +# define Q_GETNEXTQUOTA 0x800009 + +struct if_nextdqblk { + uint64_t dqb_bhardlimit; + uint64_t dqb_bsoftlimit; + uint64_t dqb_curspace; + uint64_t dqb_ihardlimit; + uint64_t dqb_isoftlimit; + uint64_t dqb_curinodes; + uint64_t dqb_btime; + uint64_t dqb_itime; + uint32_t dqb_valid; + uint32_t dqb_id; +}; +# endif /* !Q_GETNEXTQUOTA */ + +# include "xlat.h" +# include "xlat/quota_formats.h" +# include "xlat/if_dqblk_valid.h" +# include "xlat/if_dqinfo_flags.h" +# include "xlat/if_dqinfo_valid.h" + +void +print_dqblk(long rc, void *ptr, void *arg) +{ + struct if_dqblk *db = ptr; + long out_arg = (long) arg; + + if (((rc != 0) && out_arg) || (out_arg > 1)) { + printf("%p", db); + return; + } + + PRINT_FIELD_U("{", db, dqb_bhardlimit); + PRINT_FIELD_U(", ", db, dqb_bsoftlimit); + PRINT_FIELD_U(", ", db, dqb_curspace); + PRINT_FIELD_U(", ", db, dqb_ihardlimit); + PRINT_FIELD_U(", ", db, dqb_isoftlimit); + PRINT_FIELD_U(", ", db, dqb_curinodes); + +# if VERBOSE + PRINT_FIELD_U(", ", db, dqb_btime); + PRINT_FIELD_U(", ", db, dqb_itime); + + printf(", dqb_valid="); + printflags(if_dqblk_valid, db->dqb_valid, "QIF_???"); +# else + printf(", ..."); +# endif /* !VERBOSE */ + printf("}"); +} + +void +print_nextdqblk(long rc, void *ptr, void *arg) +{ + struct if_nextdqblk *db = ptr; + long out_arg = (long) arg; + + if (((rc != 0) && out_arg) || (out_arg > 1)) { + printf("%p", db); + return; + } + + PRINT_FIELD_U("{", db, dqb_bhardlimit); + PRINT_FIELD_U(", ", db, dqb_bsoftlimit); + PRINT_FIELD_U(", ", db, dqb_curspace); + PRINT_FIELD_U(", ", db, dqb_ihardlimit); + PRINT_FIELD_U(", ", db, dqb_isoftlimit); + PRINT_FIELD_U(", ", db, dqb_curinodes); + +# if VERBOSE + PRINT_FIELD_U(", ", db, dqb_btime); + PRINT_FIELD_U(", ", db, dqb_itime); + + printf(", dqb_valid="); + printflags(if_dqblk_valid, db->dqb_valid, "QIF_???"); + + PRINT_FIELD_U(", ", db, dqb_id); +# else + PRINT_FIELD_U(", ", db, dqb_id); + printf(", ..."); +# endif /* !VERBOSE */ + printf("}"); +} + +void +print_dqinfo(long rc, void *ptr, void *arg) +{ + struct if_dqinfo *di = ptr; + long out_arg = (long) arg; + + if (((rc != 0) && out_arg) || (out_arg > 1)) { + printf("%p", di); + return; + } + + PRINT_FIELD_U("{", di, dqi_bgrace); + PRINT_FIELD_U(", ", di, dqi_igrace); + + printf(", dqi_flags="); + printflags(if_dqinfo_flags, di->dqi_flags, "DQF_???"); + printf(", dqi_valid="); + printflags(if_dqinfo_valid, di->dqi_valid, "IIF_???"); + printf("}"); +} + + +int +main(void) +{ + 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)); + + + 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 00000000..49a6f87c --- /dev/null +++ b/tests/quotactl.h @@ -0,0 +1,188 @@ +/* + * Common definitions for Linux and XFS quota tests. + * + * Copyright (c) 2016 Eugene Syromiatnikov + * Copyright (c) 2016 Dmitry V. Levin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef STRACE_TESTS_QUOTACTL_H +#define STRACE_TESTS_QUOTACTL_H + +# include +# include +# include + +# ifdef HAVE_LINUX_QUOTA_H +/* Broken in CentOS 5: has extern spinlock_t dq_data_lock; declaration */ +# include +# else +# include +/* Broken in some new glibc versions: have Q_GETNEXTQUOTA definition but no + * struct nextdqblk defined. Fixed in glibc-2.24-106-g4d72808. */ +# include +# 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 PRINT_FIELD_D(prefix, where, field) \ + printf("%s%s=%lld", (prefix), #field, \ + sign_extend_unsigned_to_ll((where)->field)) + +# define PRINT_FIELD_U(prefix, where, field) \ + printf("%s%s=%llu", (prefix), #field, \ + zero_extend_signed_to_ull((where)->field)) + +# define PRINT_FIELD_X(prefix, where, field) \ + printf("%s%s=%#llx", (prefix), #field, \ + zero_extend_signed_to_ull((where)->field)) + +# 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 00000000..d0101f46 --- /dev/null +++ b/tests/quotactl.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check decoding of quotactl syscall. + +. "${srcdir=.}/init.sh" +run_strace_match_diff