162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/cred.h>
362306a36Sopenharmony_ci#include <linux/init.h>
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/quotaops.h>
662306a36Sopenharmony_ci#include <linux/sched.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <net/netlink.h>
962306a36Sopenharmony_ci#include <net/genetlink.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic const struct genl_multicast_group quota_mcgrps[] = {
1262306a36Sopenharmony_ci	{ .name = "events", },
1362306a36Sopenharmony_ci};
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Netlink family structure for quota */
1662306a36Sopenharmony_cistatic struct genl_family quota_genl_family __ro_after_init = {
1762306a36Sopenharmony_ci	.module = THIS_MODULE,
1862306a36Sopenharmony_ci	.hdrsize = 0,
1962306a36Sopenharmony_ci	.name = "VFS_DQUOT",
2062306a36Sopenharmony_ci	.version = 1,
2162306a36Sopenharmony_ci	.maxattr = QUOTA_NL_A_MAX,
2262306a36Sopenharmony_ci	.mcgrps = quota_mcgrps,
2362306a36Sopenharmony_ci	.n_mcgrps = ARRAY_SIZE(quota_mcgrps),
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * quota_send_warning - Send warning to userspace about exceeded quota
2862306a36Sopenharmony_ci * @qid: The kernel internal quota identifier.
2962306a36Sopenharmony_ci * @dev: The device on which the fs is mounted (sb->s_dev)
3062306a36Sopenharmony_ci * @warntype: The type of the warning: QUOTA_NL_...
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * This can be used by filesystems (including those which don't use
3362306a36Sopenharmony_ci * dquot) to send a message to userspace relating to quota limits.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_civoid quota_send_warning(struct kqid qid, dev_t dev,
3862306a36Sopenharmony_ci			const char warntype)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	static atomic_t seq;
4162306a36Sopenharmony_ci	struct sk_buff *skb;
4262306a36Sopenharmony_ci	void *msg_head;
4362306a36Sopenharmony_ci	int ret;
4462306a36Sopenharmony_ci	int msg_size = 4 * nla_total_size(sizeof(u32)) +
4562306a36Sopenharmony_ci		       2 * nla_total_size_64bit(sizeof(u64));
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* We have to allocate using GFP_NOFS as we are called from a
4862306a36Sopenharmony_ci	 * filesystem performing write and thus further recursion into
4962306a36Sopenharmony_ci	 * the fs to free some data could cause deadlocks. */
5062306a36Sopenharmony_ci	skb = genlmsg_new(msg_size, GFP_NOFS);
5162306a36Sopenharmony_ci	if (!skb) {
5262306a36Sopenharmony_ci		printk(KERN_ERR
5362306a36Sopenharmony_ci		  "VFS: Not enough memory to send quota warning.\n");
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
5762306a36Sopenharmony_ci			&quota_genl_family, 0, QUOTA_NL_C_WARNING);
5862306a36Sopenharmony_ci	if (!msg_head) {
5962306a36Sopenharmony_ci		printk(KERN_ERR
6062306a36Sopenharmony_ci		  "VFS: Cannot store netlink header in quota warning.\n");
6162306a36Sopenharmony_ci		goto err_out;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, qid.type);
6462306a36Sopenharmony_ci	if (ret)
6562306a36Sopenharmony_ci		goto attr_err_out;
6662306a36Sopenharmony_ci	ret = nla_put_u64_64bit(skb, QUOTA_NL_A_EXCESS_ID,
6762306a36Sopenharmony_ci				from_kqid_munged(&init_user_ns, qid),
6862306a36Sopenharmony_ci				QUOTA_NL_A_PAD);
6962306a36Sopenharmony_ci	if (ret)
7062306a36Sopenharmony_ci		goto attr_err_out;
7162306a36Sopenharmony_ci	ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
7262306a36Sopenharmony_ci	if (ret)
7362306a36Sopenharmony_ci		goto attr_err_out;
7462306a36Sopenharmony_ci	ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR, MAJOR(dev));
7562306a36Sopenharmony_ci	if (ret)
7662306a36Sopenharmony_ci		goto attr_err_out;
7762306a36Sopenharmony_ci	ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
7862306a36Sopenharmony_ci	if (ret)
7962306a36Sopenharmony_ci		goto attr_err_out;
8062306a36Sopenharmony_ci	ret = nla_put_u64_64bit(skb, QUOTA_NL_A_CAUSED_ID,
8162306a36Sopenharmony_ci				from_kuid_munged(&init_user_ns, current_uid()),
8262306a36Sopenharmony_ci				QUOTA_NL_A_PAD);
8362306a36Sopenharmony_ci	if (ret)
8462306a36Sopenharmony_ci		goto attr_err_out;
8562306a36Sopenharmony_ci	genlmsg_end(skb, msg_head);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	genlmsg_multicast(&quota_genl_family, skb, 0, 0, GFP_NOFS);
8862306a36Sopenharmony_ci	return;
8962306a36Sopenharmony_ciattr_err_out:
9062306a36Sopenharmony_ci	printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
9162306a36Sopenharmony_cierr_out:
9262306a36Sopenharmony_ci	kfree_skb(skb);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ciEXPORT_SYMBOL(quota_send_warning);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int __init quota_init(void)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	if (genl_register_family(&quota_genl_family) != 0)
9962306a36Sopenharmony_ci		printk(KERN_ERR
10062306a36Sopenharmony_ci		       "VFS: Failed to create quota netlink interface.\n");
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_cifs_initcall(quota_init);
104