summaryrefslogtreecommitdiff
path: root/fs/quota/quota.c
diff options
context:
space:
mode:
authorEric Sandeen <sandeen@redhat.com>2016-02-08 03:22:21 +0300
committerDave Chinner <david@fromorbit.com>2016-02-08 03:22:21 +0300
commit926132c0257a5a8d149a6a395cc3405e55420566 (patch)
tree5913c59e71e12e53e4002bf0e973539af13a4f89 /fs/quota/quota.c
parent8b37524962b9c54423374717786198f5c0820a28 (diff)
downloadlinux-926132c0257a5a8d149a6a395cc3405e55420566.tar.xz
quota: add new quotactl Q_GETNEXTQUOTA
Q_GETNEXTQUOTA is exactly like Q_GETQUOTA, 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. This does require a new data structure for userspace, as the current structure does not include an ID for the returned quota information. Today, Ext4 with a hidden quota inode requires 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>
Diffstat (limited to 'fs/quota/quota.c')
-rw-r--r--fs/quota/quota.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 0a6dd71ea309..0ebc90496525 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -222,6 +222,34 @@ static int quota_getquota(struct super_block *sb, int type, qid_t id,
return 0;
}
+/*
+ * Return quota for next active quota >= this id, if any exists,
+ * otherwise return -ESRCH via ->get_nextdqblk
+ */
+static int quota_getnextquota(struct super_block *sb, int type, qid_t id,
+ void __user *addr)
+{
+ struct kqid qid;
+ struct qc_dqblk fdq;
+ struct if_nextdqblk idq;
+ 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, &fdq);
+ if (ret)
+ return ret;
+ /* struct if_nextdqblk is a superset of struct if_dqblk */
+ copy_to_if_dqblk((struct if_dqblk *)&idq, &fdq);
+ idq.dqb_id = from_kqid(current_user_ns(), qid);
+ if (copy_to_user(addr, &idq, sizeof(idq)))
+ return -EFAULT;
+ return 0;
+}
+
static void copy_from_if_dqblk(struct qc_dqblk *dst, struct if_dqblk *src)
{
dst->d_spc_hardlimit = qbtos(src->dqb_bhardlimit);
@@ -698,6 +726,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
return quota_setinfo(sb, type, addr);
case Q_GETQUOTA:
return quota_getquota(sb, type, id, addr);
+ case Q_GETNEXTQUOTA:
+ return quota_getnextquota(sb, type, id, addr);
case Q_SETQUOTA:
return quota_setquota(sb, type, id, addr);
case Q_SYNC:
@@ -738,6 +768,7 @@ static int quotactl_cmd_write(int cmd)
switch (cmd) {
case Q_GETFMT:
case Q_GETINFO:
+ case Q_GETNEXTQUOTA:
case Q_SYNC:
case Q_XGETQSTAT:
case Q_XGETQSTATV: