quota: add new quotactl Q_XGETNEXTQUOTA

Q_XGETNEXTQUOTA is exactly like Q_XGETQUOTA, except that it
will return quota information for the id equal to or greater
than the id requested.  In other words, if the requested id has
no quota, the command will return quota information for the
next higher id which does have a quota set.  If no higher id
has an active quota, -ESRCH is returned.

This allows filesystems to do efficient iteration in kernelspace,
much like extN filesystems do in userspace when asked to report
all active quotas.

The patch adds a d_id field to struct qc_dqblk so that we can
pass back the id of the quota which was found, and return it
to userspace.

Today, filesystems such as XFS require getpwent-style iterations,
and for systems which have i.e. LDAP backends, this can be very
slow, or even impossible if iteration is not allowed in the
configuration.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Eric Sandeen 2016-02-08 11:21:50 +11:00 committed by Dave Chinner
parent 3218a3ec87
commit 8b37524962
3 changed files with 34 additions and 0 deletions

View File

@ -625,6 +625,34 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id,
return ret; return ret;
} }
/*
* Return quota for next active quota >= this id, if any exists,
* otherwise return -ESRCH via ->get_nextdqblk.
*/
static int quota_getnextxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
struct qc_dqblk qdq;
struct kqid qid;
qid_t id_out;
int ret;
if (!sb->s_qcop->get_nextdqblk)
return -ENOSYS;
qid = make_kqid(current_user_ns(), type, id);
if (!qid_valid(qid))
return -EINVAL;
ret = sb->s_qcop->get_nextdqblk(sb, &qid, &qdq);
if (ret)
return ret;
id_out = from_kqid(current_user_ns(), qid);
copy_to_xfs_dqblk(&fdq, &qdq, type, id_out);
if (copy_to_user(addr, &fdq, sizeof(fdq)))
return -EFAULT;
return ret;
}
static int quota_rmxquota(struct super_block *sb, void __user *addr) static int quota_rmxquota(struct super_block *sb, void __user *addr)
{ {
__u32 flags; __u32 flags;
@ -690,6 +718,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
return quota_setxquota(sb, type, id, addr); return quota_setxquota(sb, type, id, addr);
case Q_XGETQUOTA: case Q_XGETQUOTA:
return quota_getxquota(sb, type, id, addr); return quota_getxquota(sb, type, id, addr);
case Q_XGETNEXTQUOTA:
return quota_getnextxquota(sb, type, id, addr);
case Q_XQUOTASYNC: case Q_XQUOTASYNC:
if (sb->s_flags & MS_RDONLY) if (sb->s_flags & MS_RDONLY)
return -EROFS; return -EROFS;
@ -712,6 +742,7 @@ static int quotactl_cmd_write(int cmd)
case Q_XGETQSTAT: case Q_XGETQSTAT:
case Q_XGETQSTATV: case Q_XGETQSTATV:
case Q_XGETQUOTA: case Q_XGETQUOTA:
case Q_XGETNEXTQUOTA:
case Q_XQUOTASYNC: case Q_XQUOTASYNC:
return 0; return 0;
} }

View File

@ -425,6 +425,8 @@ struct quotactl_ops {
int (*quota_sync)(struct super_block *, int); int (*quota_sync)(struct super_block *, int);
int (*set_info)(struct super_block *, int, struct qc_info *); int (*set_info)(struct super_block *, int, struct qc_info *);
int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *);
int (*get_nextdqblk)(struct super_block *, struct kqid *,
struct qc_dqblk *);
int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *);
int (*get_state)(struct super_block *, struct qc_state *); int (*get_state)(struct super_block *, struct qc_state *);
int (*rm_xquota)(struct super_block *, unsigned int); int (*rm_xquota)(struct super_block *, unsigned int);

View File

@ -39,6 +39,7 @@
#define Q_XQUOTARM XQM_CMD(6) /* free disk space used by dquots */ #define Q_XQUOTARM XQM_CMD(6) /* free disk space used by dquots */
#define Q_XQUOTASYNC XQM_CMD(7) /* delalloc flush, updates dquots */ #define Q_XQUOTASYNC XQM_CMD(7) /* delalloc flush, updates dquots */
#define Q_XGETQSTATV XQM_CMD(8) /* newer version of get quota */ #define Q_XGETQSTATV XQM_CMD(8) /* newer version of get quota */
#define Q_XGETNEXTQUOTA XQM_CMD(9) /* get disk limits and usage >= ID */
/* /*
* fs_disk_quota structure: * fs_disk_quota structure: