1// SPDX-License-Identifier: GPL-2.0-only
2#include <linux/errno.h>
3#include <linux/fs.h>
4#include <linux/quota.h>
5#include <linux/quotaops.h>
6#include <linux/dqblk_v1.h>
7#include <linux/kernel.h>
8#include <linux/init.h>
9#include <linux/module.h>
10
11#include <asm/byteorder.h>
12
13#include "quotaio_v1.h"
14
15MODULE_AUTHOR("Jan Kara");
16MODULE_DESCRIPTION("Old quota format support");
17MODULE_LICENSE("GPL");
18
19#define QUOTABLOCK_BITS 10
20#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
21
22static inline qsize_t v1_stoqb(qsize_t space)
23{
24	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
25}
26
27static inline qsize_t v1_qbtos(qsize_t blocks)
28{
29	return blocks << QUOTABLOCK_BITS;
30}
31
32static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
33{
34	m->dqb_ihardlimit = d->dqb_ihardlimit;
35	m->dqb_isoftlimit = d->dqb_isoftlimit;
36	m->dqb_curinodes = d->dqb_curinodes;
37	m->dqb_bhardlimit = v1_qbtos(d->dqb_bhardlimit);
38	m->dqb_bsoftlimit = v1_qbtos(d->dqb_bsoftlimit);
39	m->dqb_curspace = v1_qbtos(d->dqb_curblocks);
40	m->dqb_itime = d->dqb_itime;
41	m->dqb_btime = d->dqb_btime;
42}
43
44static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
45{
46	d->dqb_ihardlimit = m->dqb_ihardlimit;
47	d->dqb_isoftlimit = m->dqb_isoftlimit;
48	d->dqb_curinodes = m->dqb_curinodes;
49	d->dqb_bhardlimit = v1_stoqb(m->dqb_bhardlimit);
50	d->dqb_bsoftlimit = v1_stoqb(m->dqb_bsoftlimit);
51	d->dqb_curblocks = v1_stoqb(m->dqb_curspace);
52	d->dqb_itime = m->dqb_itime;
53	d->dqb_btime = m->dqb_btime;
54}
55
56static int v1_read_dqblk(struct dquot *dquot)
57{
58	int type = dquot->dq_id.type;
59	struct v1_disk_dqblk dqblk;
60	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
61
62	if (!dqopt->files[type])
63		return -EINVAL;
64
65	/* Set structure to 0s in case read fails/is after end of file */
66	memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
67	dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
68			sizeof(struct v1_disk_dqblk),
69			v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
70
71	v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
72	if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
73	    dquot->dq_dqb.dqb_bsoftlimit == 0 &&
74	    dquot->dq_dqb.dqb_ihardlimit == 0 &&
75	    dquot->dq_dqb.dqb_isoftlimit == 0)
76		set_bit(DQ_FAKE_B, &dquot->dq_flags);
77	dqstats_inc(DQST_READS);
78
79	return 0;
80}
81
82static int v1_commit_dqblk(struct dquot *dquot)
83{
84	short type = dquot->dq_id.type;
85	ssize_t ret;
86	struct v1_disk_dqblk dqblk;
87
88	v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
89	if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) ||
90	    ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) {
91		dqblk.dqb_btime =
92			sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
93		dqblk.dqb_itime =
94			sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
95	}
96	ret = 0;
97	if (sb_dqopt(dquot->dq_sb)->files[type])
98		ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
99			(char *)&dqblk, sizeof(struct v1_disk_dqblk),
100			v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
101	if (ret != sizeof(struct v1_disk_dqblk)) {
102		quota_error(dquot->dq_sb, "dquota write failed");
103		if (ret >= 0)
104			ret = -EIO;
105		goto out;
106	}
107	ret = 0;
108
109out:
110	dqstats_inc(DQST_WRITES);
111
112	return ret;
113}
114
115/* Magics of new quota format */
116#define V2_INITQMAGICS {\
117	0xd9c01f11,     /* USRQUOTA */\
118	0xd9c01927      /* GRPQUOTA */\
119}
120
121/* Header of new quota format */
122struct v2_disk_dqheader {
123	__le32 dqh_magic;        /* Magic number identifying file */
124	__le32 dqh_version;      /* File version */
125};
126
127static int v1_check_quota_file(struct super_block *sb, int type)
128{
129	struct inode *inode = sb_dqopt(sb)->files[type];
130	ulong blocks;
131	size_t off;
132	struct v2_disk_dqheader dqhead;
133	ssize_t size;
134	loff_t isize;
135	static const uint quota_magics[] = V2_INITQMAGICS;
136
137	isize = i_size_read(inode);
138	if (!isize)
139		return 0;
140	blocks = isize >> BLOCK_SIZE_BITS;
141	off = isize & (BLOCK_SIZE - 1);
142	if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) %
143	    sizeof(struct v1_disk_dqblk))
144		return 0;
145	/* Doublecheck whether we didn't get file with new format - with old
146	 * quotactl() this could happen */
147	size = sb->s_op->quota_read(sb, type, (char *)&dqhead,
148				    sizeof(struct v2_disk_dqheader), 0);
149	if (size != sizeof(struct v2_disk_dqheader))
150		return 1;	/* Probably not new format */
151	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
152		return 1;	/* Definitely not new format */
153	printk(KERN_INFO
154	       "VFS: %s: Refusing to turn on old quota format on given file."
155	       " It probably contains newer quota format.\n", sb->s_id);
156        return 0;		/* Seems like a new format file -> refuse it */
157}
158
159static int v1_read_file_info(struct super_block *sb, int type)
160{
161	struct quota_info *dqopt = sb_dqopt(sb);
162	struct v1_disk_dqblk dqblk;
163	unsigned int memalloc;
164	int ret;
165
166	down_read(&dqopt->dqio_sem);
167	memalloc = memalloc_nofs_save();
168	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
169				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
170	if (ret != sizeof(struct v1_disk_dqblk)) {
171		if (ret >= 0)
172			ret = -EIO;
173		goto out;
174	}
175	ret = 0;
176	/* limits are stored as unsigned 32-bit data */
177	dqopt->info[type].dqi_max_spc_limit = 0xffffffffULL << QUOTABLOCK_BITS;
178	dqopt->info[type].dqi_max_ino_limit = 0xffffffff;
179	dqopt->info[type].dqi_igrace =
180			dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
181	dqopt->info[type].dqi_bgrace =
182			dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
183out:
184	memalloc_nofs_restore(memalloc);
185	up_read(&dqopt->dqio_sem);
186	return ret;
187}
188
189static int v1_write_file_info(struct super_block *sb, int type)
190{
191	struct quota_info *dqopt = sb_dqopt(sb);
192	struct v1_disk_dqblk dqblk;
193	unsigned int memalloc;
194	int ret;
195
196	down_write(&dqopt->dqio_sem);
197	memalloc = memalloc_nofs_save();
198	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
199				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
200	if (ret != sizeof(struct v1_disk_dqblk)) {
201		if (ret >= 0)
202			ret = -EIO;
203		goto out;
204	}
205	spin_lock(&dq_data_lock);
206	dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
207	dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
208	dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
209	spin_unlock(&dq_data_lock);
210	ret = sb->s_op->quota_write(sb, type, (char *)&dqblk,
211	      sizeof(struct v1_disk_dqblk), v1_dqoff(0));
212	if (ret == sizeof(struct v1_disk_dqblk))
213		ret = 0;
214	else if (ret >= 0)
215		ret = -EIO;
216out:
217	memalloc_nofs_restore(memalloc);
218	up_write(&dqopt->dqio_sem);
219	return ret;
220}
221
222static const struct quota_format_ops v1_format_ops = {
223	.check_quota_file	= v1_check_quota_file,
224	.read_file_info		= v1_read_file_info,
225	.write_file_info	= v1_write_file_info,
226	.read_dqblk		= v1_read_dqblk,
227	.commit_dqblk		= v1_commit_dqblk,
228};
229
230static struct quota_format_type v1_quota_format = {
231	.qf_fmt_id	= QFMT_VFS_OLD,
232	.qf_ops		= &v1_format_ops,
233	.qf_owner	= THIS_MODULE
234};
235
236static int __init init_v1_quota_format(void)
237{
238        return register_quota_format(&v1_quota_format);
239}
240
241static void __exit exit_v1_quota_format(void)
242{
243        unregister_quota_format(&v1_quota_format);
244}
245
246module_init(init_v1_quota_format);
247module_exit(exit_v1_quota_format);
248
249