diff options
Diffstat (limited to 'src/quota.c')
-rw-r--r-- | src/quota.c | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/src/quota.c b/src/quota.c new file mode 100644 index 000000000..5168b3dac --- /dev/null +++ b/src/quota.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl> + * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl> + * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com> + * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl> + * Copyright (c) 2005-2016 Dmitry V. Levin <ldv@strace.io> + * Copyright (c) 2006-2018 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "defs.h" +#include "xfs_quota_stat.h" + +#define SUBCMDMASK 0x00ff +#define SUBCMDSHIFT 8 +#define QCMD_CMD(cmd) ((uint32_t)(cmd) >> SUBCMDSHIFT) +#define QCMD_TYPE(cmd) ((uint32_t)(cmd) & SUBCMDMASK) + +#define OLD_CMD(cmd) ((uint32_t)(cmd) << SUBCMDSHIFT) +#define NEW_CMD(cmd) ((uint32_t)(cmd) | 0x800000) +#define XQM_CMD(cmd) ((uint32_t)(cmd) | ('X' << SUBCMDSHIFT)) + +#include "xlat/quotacmds.h" +#include "xlat/quotatypes.h" +#include "xlat/quota_formats.h" +#include "xlat/xfs_quota_flags.h" +#include "xlat/xfs_dqblk_flags.h" +#include "xlat/if_dqblk_valid.h" +#include "xlat/if_dqinfo_flags.h" +#include "xlat/if_dqinfo_valid.h" + +/* + * We add attribute packed due to the fact that the structure is 8-byte aligned + * on 64-bit systems and therefore has additional 4 bytes of padding, which + * leads to problems when it is used on 32-bit tracee which does not have such + * padding. + */ +struct if_dqblk { + 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; +} ATTRIBUTE_PACKED; + +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; +}; + +struct xfs_dqblk { + int8_t d_version; /* version of this structure */ + uint8_t d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */ + uint16_t d_fieldmask; /* field specifier */ + uint32_t d_id; /* user, project, or group ID */ + uint64_t d_blk_hardlimit; /* absolute limit on disk blks */ + uint64_t d_blk_softlimit; /* preferred limit on disk blks */ + uint64_t d_ino_hardlimit; /* maximum # allocated inodes */ + uint64_t d_ino_softlimit; /* preferred inode limit */ + uint64_t d_bcount; /* # disk blocks owned by the user */ + uint64_t d_icount; /* # inodes owned by the user */ + int32_t d_itimer; /* zero if within inode limits */ + int32_t d_btimer; /* similar to above; for disk blocks */ + uint16_t d_iwarns; /* # warnings issued wrt num inodes */ + uint16_t d_bwarns; /* # warnings issued wrt disk blocks */ + int32_t d_padding2; /* padding2 - for future use */ + uint64_t d_rtb_hardlimit; /* absolute limit on realtime blks */ + uint64_t d_rtb_softlimit; /* preferred limit on RT disk blks */ + uint64_t d_rtbcount; /* # realtime blocks owned */ + int32_t d_rtbtimer; /* similar to above; for RT disk blks */ + uint16_t d_rtbwarns; /* # warnings issued wrt RT disk blks */ + int16_t d_padding3; /* padding3 - for future use */ + char d_padding4[8]; /* yet more padding */ +}; + +struct if_dqinfo { + uint64_t dqi_bgrace; + uint64_t dqi_igrace; + uint32_t dqi_flags; + uint32_t dqi_valid; +}; + +struct fs_qfilestatv { + uint64_t qfs_ino, qfs_nblks; + uint32_t qfs_nextents, qfs_pad; +}; + +struct fs_quota_statv { + int8_t qs_version; + uint8_t qs_pad1; + uint16_t qs_flags; + uint32_t qs_incoredqs; + struct fs_qfilestatv qs_uquota; + struct fs_qfilestatv qs_gquota; + struct fs_qfilestatv qs_pquota; + int32_t qs_btimelimit; + int32_t qs_itimelimit; + int32_t qs_rtbtimelimit; + uint16_t qs_bwarnlimit; + uint16_t qs_iwarnlimit; + uint64_t qs_pad2[8]; +}; + +static void +print_fs_qfilestat(const struct fs_qfilestat *const p) +{ + tprint_struct_begin(); + PRINT_FIELD_U(*p, qfs_ino); + tprint_struct_next(); + PRINT_FIELD_U(*p, qfs_nblks); + tprint_struct_next(); + PRINT_FIELD_U(*p, qfs_nextents); + tprint_struct_end(); +} + +static void +print_fs_qfilestatv(const struct fs_qfilestatv *const p) +{ + tprint_struct_begin(); + PRINT_FIELD_U(*p, qfs_ino); + tprint_struct_next(); + PRINT_FIELD_U(*p, qfs_nblks); + tprint_struct_next(); + PRINT_FIELD_U(*p, qfs_nextents); + tprint_struct_end(); +} + +static int +decode_cmd_data(struct tcb *tcp, uint32_t id, uint32_t cmd, kernel_ulong_t data) +{ + switch (cmd) { + case Q_QUOTAOFF: + case Q_SYNC: + case Q_XQUOTASYNC: + break; + case Q_QUOTAON: + tprints(", "); + printxval(quota_formats, id, "QFMT_VFS_???"); + tprints(", "); + printpath(tcp, data); + break; + case Q_GETQUOTA: + if (entering(tcp)) { + printuid(", ", id); + tprints(", "); + + return 0; + } + + ATTRIBUTE_FALLTHROUGH; + case Q_SETQUOTA: + { + struct if_dqblk dq; + + if (entering(tcp)) { + printuid(", ", id); + tprints(", "); + } + + if (umove_or_printaddr(tcp, data, &dq)) + break; + tprint_struct_begin(); + PRINT_FIELD_U(dq, dqb_bhardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_bsoftlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_curspace); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_ihardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_isoftlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_curinodes); + if (!abbrev(tcp)) { + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_btime); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_itime); + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, dqb_valid, + if_dqblk_valid, "QIF_???"); + } else { + tprint_struct_next(); + tprint_more_data_follows(); + } + tprint_struct_end(); + break; + } + case Q_GETNEXTQUOTA: + { + struct if_nextdqblk dq; + + if (entering(tcp)) { + printuid(", ", id); + tprints(", "); + + return 0; + } + + if (umove_or_printaddr(tcp, data, &dq)) + break; + tprint_struct_begin(); + PRINT_FIELD_U(dq, dqb_bhardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_bsoftlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_curspace); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_ihardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_isoftlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_curinodes); + if (!abbrev(tcp)) { + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_btime); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_itime); + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, dqb_valid, + if_dqblk_valid, "QIF_???"); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_id); + } else { + tprint_struct_next(); + PRINT_FIELD_U(dq, dqb_id); + tprint_struct_next(); + tprint_more_data_follows(); + } + tprint_struct_end(); + break; + } + case Q_XGETQUOTA: + case Q_XGETNEXTQUOTA: + if (entering(tcp)) { + printuid(", ", id); + tprints(", "); + + return 0; + } + + ATTRIBUTE_FALLTHROUGH; + case Q_XSETQLIM: + { + struct xfs_dqblk dq; + + if (entering(tcp)) { + printuid(", ", id); + tprints(", "); + } + + if (umove_or_printaddr(tcp, data, &dq)) + break; + tprint_struct_begin(); + PRINT_FIELD_D(dq, d_version); + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, d_flags, + xfs_dqblk_flags, "XFS_???_QUOTA"); + tprint_struct_next(); + PRINT_FIELD_X(dq, d_fieldmask); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_id); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_blk_hardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_blk_softlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_ino_hardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_ino_softlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_bcount); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_icount); + if (!abbrev(tcp)) { + tprint_struct_next(); + PRINT_FIELD_D(dq, d_itimer); + tprint_struct_next(); + PRINT_FIELD_D(dq, d_btimer); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_iwarns); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_bwarns); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_rtb_hardlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_rtb_softlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_rtbcount); + tprint_struct_next(); + PRINT_FIELD_D(dq, d_rtbtimer); + tprint_struct_next(); + PRINT_FIELD_U(dq, d_rtbwarns); + } else { + tprint_struct_next(); + tprint_more_data_follows(); + } + tprint_struct_end(); + break; + } + case Q_GETFMT: + { + uint32_t fmt; + + if (entering(tcp)) { + tprints(", "); + + return 0; + } + + if (umove_or_printaddr(tcp, data, &fmt)) + break; + tprints("["); + printxval(quota_formats, fmt, "QFMT_VFS_???"); + tprints("]"); + break; + } + case Q_GETINFO: + if (entering(tcp)) { + tprints(", "); + + return 0; + } + + ATTRIBUTE_FALLTHROUGH; + case Q_SETINFO: + { + struct if_dqinfo dq; + + if (entering(tcp)) + tprints(", "); + + if (umove_or_printaddr(tcp, data, &dq)) + break; + tprint_struct_begin(); + PRINT_FIELD_U(dq, dqi_bgrace); + tprint_struct_next(); + PRINT_FIELD_U(dq, dqi_igrace); + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, dqi_flags, if_dqinfo_flags, "DQF_???"); + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, dqi_valid, if_dqinfo_valid, "IIF_???"); + tprint_struct_end(); + break; + } + case Q_XGETQSTAT: + { + struct xfs_dqstats dq; + + if (entering(tcp)) { + tprints(", "); + + return 0; + } + if (fetch_struct_quotastat(tcp, data, &dq)) { + tprint_struct_begin(); + PRINT_FIELD_D(dq, qs_version); + if (!abbrev(tcp)) { + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, qs_flags, + xfs_quota_flags, "XFS_QUOTA_???"); + tprint_struct_next(); + PRINT_FIELD_OBJ_PTR(dq, qs_uquota, + print_fs_qfilestat); + tprint_struct_next(); + PRINT_FIELD_OBJ_PTR(dq, qs_gquota, + print_fs_qfilestat); + tprint_struct_next(); + PRINT_FIELD_U(dq, qs_incoredqs); + tprint_struct_next(); + PRINT_FIELD_D(dq, qs_btimelimit); + tprint_struct_next(); + PRINT_FIELD_D(dq, qs_itimelimit); + tprint_struct_next(); + PRINT_FIELD_D(dq, qs_rtbtimelimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, qs_bwarnlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, qs_iwarnlimit); + } else { + tprint_struct_next(); + tprint_more_data_follows(); + } + tprint_struct_end(); + } + break; + } + case Q_XGETQSTATV: + { + struct fs_quota_statv dq; + + if (entering(tcp)) { + tprints(", "); + + return 0; + } + + if (umove_or_printaddr(tcp, data, &dq)) + break; + tprint_struct_begin(); + PRINT_FIELD_D(dq, qs_version); + if (!abbrev(tcp)) { + tprint_struct_next(); + PRINT_FIELD_FLAGS(dq, qs_flags, + xfs_quota_flags, "XFS_QUOTA_???"); + tprint_struct_next(); + PRINT_FIELD_U(dq, qs_incoredqs); + tprint_struct_next(); + PRINT_FIELD_OBJ_PTR(dq, qs_uquota, + print_fs_qfilestatv); + tprint_struct_next(); + PRINT_FIELD_OBJ_PTR(dq, qs_gquota, + print_fs_qfilestatv); + tprint_struct_next(); + PRINT_FIELD_OBJ_PTR(dq, qs_pquota, + print_fs_qfilestatv); + tprint_struct_next(); + PRINT_FIELD_D(dq, qs_btimelimit); + tprint_struct_next(); + PRINT_FIELD_D(dq, qs_itimelimit); + tprint_struct_next(); + PRINT_FIELD_D(dq, qs_rtbtimelimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, qs_bwarnlimit); + tprint_struct_next(); + PRINT_FIELD_U(dq, qs_iwarnlimit); + } else { + tprint_struct_next(); + tprint_more_data_follows(); + } + tprint_struct_end(); + break; + } + case Q_XQUOTAON: + case Q_XQUOTAOFF: + { + uint32_t flag; + + tprints(", "); + + if (umove_or_printaddr(tcp, data, &flag)) + break; + tprints("["); + printflags(xfs_quota_flags, flag, "XFS_QUOTA_???"); + tprints("]"); + break; + } + case Q_XQUOTARM: + { + uint32_t flag; + + tprints(", "); + + if (umove_or_printaddr(tcp, data, &flag)) + break; + tprints("["); + printflags(xfs_dqblk_flags, flag, "XFS_???_QUOTA"); + tprints("]"); + break; + } + default: + printuid(", ", id); + tprints(", "); + printaddr(data); + break; + } + return RVAL_DECODED; +} + +static void +print_qcmd(const uint32_t qcmd) +{ + const uint32_t cmd = QCMD_CMD(qcmd); + const uint32_t type = QCMD_TYPE(qcmd); + + if (xlat_verbose(xlat_verbosity) != XLAT_STYLE_ABBREV) + tprintf("%u", qcmd); + + if (xlat_verbose(xlat_verbosity) == XLAT_STYLE_RAW) + return; + + if (xlat_verbose(xlat_verbosity) == XLAT_STYLE_VERBOSE) + tprints(" /* "); + + tprints("QCMD("); + printxvals_ex(cmd, "Q_???", XLAT_STYLE_ABBREV, quotacmds, NULL); + tprints(", "); + printxvals_ex(type, "???QUOTA", XLAT_STYLE_ABBREV, quotatypes, NULL); + tprints(")"); + + if (xlat_verbose(xlat_verbosity) == XLAT_STYLE_VERBOSE) + tprints(" */"); +} + +SYS_FUNC(quotactl) +{ + /* + * The Linux kernel only looks at the low 32 bits of command and id + * arguments, but on some 64-bit architectures (s390x) this word + * will have been sign-extended when we see it. The high 1 bits + * don't mean anything, so don't confuse the output with them. + */ + uint32_t qcmd = tcp->u_arg[0]; + uint32_t cmd = QCMD_CMD(qcmd); + uint32_t id = tcp->u_arg[2]; + + if (entering(tcp)) { + print_qcmd(qcmd); + tprints(", "); + printpath(tcp, tcp->u_arg[1]); + } + return decode_cmd_data(tcp, id, cmd, tcp->u_arg[3]); +} |