xref: /openbmc/linux/fs/quota/quota_v1.c (revision f8107c99)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2884d179dSJan Kara #include <linux/errno.h>
3884d179dSJan Kara #include <linux/fs.h>
4884d179dSJan Kara #include <linux/quota.h>
5884d179dSJan Kara #include <linux/quotaops.h>
6884d179dSJan Kara #include <linux/dqblk_v1.h>
7884d179dSJan Kara #include <linux/kernel.h>
8884d179dSJan Kara #include <linux/init.h>
9884d179dSJan Kara #include <linux/module.h>
10884d179dSJan Kara 
11884d179dSJan Kara #include <asm/byteorder.h>
12884d179dSJan Kara 
13884d179dSJan Kara #include "quotaio_v1.h"
14884d179dSJan Kara 
15884d179dSJan Kara MODULE_AUTHOR("Jan Kara");
16884d179dSJan Kara MODULE_DESCRIPTION("Old quota format support");
17884d179dSJan Kara MODULE_LICENSE("GPL");
18884d179dSJan Kara 
19884d179dSJan Kara #define QUOTABLOCK_BITS 10
20884d179dSJan Kara #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
21884d179dSJan Kara 
v1_stoqb(qsize_t space)22884d179dSJan Kara static inline qsize_t v1_stoqb(qsize_t space)
23884d179dSJan Kara {
24884d179dSJan Kara 	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
25884d179dSJan Kara }
26884d179dSJan Kara 
v1_qbtos(qsize_t blocks)27884d179dSJan Kara static inline qsize_t v1_qbtos(qsize_t blocks)
28884d179dSJan Kara {
29884d179dSJan Kara 	return blocks << QUOTABLOCK_BITS;
30884d179dSJan Kara }
31884d179dSJan Kara 
v1_disk2mem_dqblk(struct mem_dqblk * m,struct v1_disk_dqblk * d)32884d179dSJan Kara static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
33884d179dSJan Kara {
34884d179dSJan Kara 	m->dqb_ihardlimit = d->dqb_ihardlimit;
35884d179dSJan Kara 	m->dqb_isoftlimit = d->dqb_isoftlimit;
36884d179dSJan Kara 	m->dqb_curinodes = d->dqb_curinodes;
37884d179dSJan Kara 	m->dqb_bhardlimit = v1_qbtos(d->dqb_bhardlimit);
38884d179dSJan Kara 	m->dqb_bsoftlimit = v1_qbtos(d->dqb_bsoftlimit);
39884d179dSJan Kara 	m->dqb_curspace = v1_qbtos(d->dqb_curblocks);
40884d179dSJan Kara 	m->dqb_itime = d->dqb_itime;
41884d179dSJan Kara 	m->dqb_btime = d->dqb_btime;
42884d179dSJan Kara }
43884d179dSJan Kara 
v1_mem2disk_dqblk(struct v1_disk_dqblk * d,struct mem_dqblk * m)44884d179dSJan Kara static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
45884d179dSJan Kara {
46884d179dSJan Kara 	d->dqb_ihardlimit = m->dqb_ihardlimit;
47884d179dSJan Kara 	d->dqb_isoftlimit = m->dqb_isoftlimit;
48884d179dSJan Kara 	d->dqb_curinodes = m->dqb_curinodes;
49884d179dSJan Kara 	d->dqb_bhardlimit = v1_stoqb(m->dqb_bhardlimit);
50884d179dSJan Kara 	d->dqb_bsoftlimit = v1_stoqb(m->dqb_bsoftlimit);
51884d179dSJan Kara 	d->dqb_curblocks = v1_stoqb(m->dqb_curspace);
52884d179dSJan Kara 	d->dqb_itime = m->dqb_itime;
53884d179dSJan Kara 	d->dqb_btime = m->dqb_btime;
54884d179dSJan Kara }
55884d179dSJan Kara 
v1_read_dqblk(struct dquot * dquot)56884d179dSJan Kara static int v1_read_dqblk(struct dquot *dquot)
57884d179dSJan Kara {
584c376dcaSEric W. Biederman 	int type = dquot->dq_id.type;
59884d179dSJan Kara 	struct v1_disk_dqblk dqblk;
60e342e38dSJan Kara 	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
61884d179dSJan Kara 
62e342e38dSJan Kara 	if (!dqopt->files[type])
63884d179dSJan Kara 		return -EINVAL;
64884d179dSJan Kara 
65884d179dSJan Kara 	/* Set structure to 0s in case read fails/is after end of file */
66884d179dSJan Kara 	memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
67268157baSJan Kara 	dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
684c376dcaSEric W. Biederman 			sizeof(struct v1_disk_dqblk),
694c376dcaSEric W. Biederman 			v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
70884d179dSJan Kara 
71884d179dSJan Kara 	v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
72268157baSJan Kara 	if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
73268157baSJan Kara 	    dquot->dq_dqb.dqb_bsoftlimit == 0 &&
74268157baSJan Kara 	    dquot->dq_dqb.dqb_ihardlimit == 0 &&
75268157baSJan Kara 	    dquot->dq_dqb.dqb_isoftlimit == 0)
76884d179dSJan Kara 		set_bit(DQ_FAKE_B, &dquot->dq_flags);
77dde95888SDmitry Monakhov 	dqstats_inc(DQST_READS);
78884d179dSJan Kara 
79884d179dSJan Kara 	return 0;
80884d179dSJan Kara }
81884d179dSJan Kara 
v1_commit_dqblk(struct dquot * dquot)82884d179dSJan Kara static int v1_commit_dqblk(struct dquot *dquot)
83884d179dSJan Kara {
844c376dcaSEric W. Biederman 	short type = dquot->dq_id.type;
85884d179dSJan Kara 	ssize_t ret;
86884d179dSJan Kara 	struct v1_disk_dqblk dqblk;
87884d179dSJan Kara 
88884d179dSJan Kara 	v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
894c376dcaSEric W. Biederman 	if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) ||
904c376dcaSEric W. Biederman 	    ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) {
91268157baSJan Kara 		dqblk.dqb_btime =
92268157baSJan Kara 			sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
93268157baSJan Kara 		dqblk.dqb_itime =
94268157baSJan Kara 			sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
95884d179dSJan Kara 	}
96884d179dSJan Kara 	ret = 0;
97884d179dSJan Kara 	if (sb_dqopt(dquot->dq_sb)->files[type])
98268157baSJan Kara 		ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
99268157baSJan Kara 			(char *)&dqblk, sizeof(struct v1_disk_dqblk),
1004c376dcaSEric W. Biederman 			v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
101884d179dSJan Kara 	if (ret != sizeof(struct v1_disk_dqblk)) {
102fb5ffb0eSJiaying Zhang 		quota_error(dquot->dq_sb, "dquota write failed");
103884d179dSJan Kara 		if (ret >= 0)
104884d179dSJan Kara 			ret = -EIO;
105884d179dSJan Kara 		goto out;
106884d179dSJan Kara 	}
107884d179dSJan Kara 	ret = 0;
108884d179dSJan Kara 
109884d179dSJan Kara out:
110dde95888SDmitry Monakhov 	dqstats_inc(DQST_WRITES);
111884d179dSJan Kara 
112884d179dSJan Kara 	return ret;
113884d179dSJan Kara }
114884d179dSJan Kara 
115884d179dSJan Kara /* Magics of new quota format */
116884d179dSJan Kara #define V2_INITQMAGICS {\
117884d179dSJan Kara 	0xd9c01f11,     /* USRQUOTA */\
118884d179dSJan Kara 	0xd9c01927      /* GRPQUOTA */\
119884d179dSJan Kara }
120884d179dSJan Kara 
121884d179dSJan Kara /* Header of new quota format */
122884d179dSJan Kara struct v2_disk_dqheader {
123884d179dSJan Kara 	__le32 dqh_magic;        /* Magic number identifying file */
124884d179dSJan Kara 	__le32 dqh_version;      /* File version */
125884d179dSJan Kara };
126884d179dSJan Kara 
v1_check_quota_file(struct super_block * sb,int type)127884d179dSJan Kara static int v1_check_quota_file(struct super_block *sb, int type)
128884d179dSJan Kara {
129884d179dSJan Kara 	struct inode *inode = sb_dqopt(sb)->files[type];
130884d179dSJan Kara 	ulong blocks;
131884d179dSJan Kara 	size_t off;
132884d179dSJan Kara 	struct v2_disk_dqheader dqhead;
133884d179dSJan Kara 	ssize_t size;
134884d179dSJan Kara 	loff_t isize;
135884d179dSJan Kara 	static const uint quota_magics[] = V2_INITQMAGICS;
136884d179dSJan Kara 
137884d179dSJan Kara 	isize = i_size_read(inode);
138884d179dSJan Kara 	if (!isize)
139884d179dSJan Kara 		return 0;
140884d179dSJan Kara 	blocks = isize >> BLOCK_SIZE_BITS;
141884d179dSJan Kara 	off = isize & (BLOCK_SIZE - 1);
142268157baSJan Kara 	if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) %
143268157baSJan Kara 	    sizeof(struct v1_disk_dqblk))
144884d179dSJan Kara 		return 0;
145268157baSJan Kara 	/* Doublecheck whether we didn't get file with new format - with old
146268157baSJan Kara 	 * quotactl() this could happen */
147268157baSJan Kara 	size = sb->s_op->quota_read(sb, type, (char *)&dqhead,
148268157baSJan Kara 				    sizeof(struct v2_disk_dqheader), 0);
149884d179dSJan Kara 	if (size != sizeof(struct v2_disk_dqheader))
150884d179dSJan Kara 		return 1;	/* Probably not new format */
151884d179dSJan Kara 	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
152884d179dSJan Kara 		return 1;	/* Definitely not new format */
153268157baSJan Kara 	printk(KERN_INFO
154268157baSJan Kara 	       "VFS: %s: Refusing to turn on old quota format on given file."
155268157baSJan Kara 	       " It probably contains newer quota format.\n", sb->s_id);
156884d179dSJan Kara         return 0;		/* Seems like a new format file -> refuse it */
157884d179dSJan Kara }
158884d179dSJan Kara 
v1_read_file_info(struct super_block * sb,int type)159884d179dSJan Kara static int v1_read_file_info(struct super_block *sb, int type)
160884d179dSJan Kara {
161884d179dSJan Kara 	struct quota_info *dqopt = sb_dqopt(sb);
162884d179dSJan Kara 	struct v1_disk_dqblk dqblk;
163884d179dSJan Kara 	int ret;
164884d179dSJan Kara 
16542fdb858SJan Kara 	down_read(&dqopt->dqio_sem);
166268157baSJan Kara 	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
167268157baSJan Kara 				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
168268157baSJan Kara 	if (ret != sizeof(struct v1_disk_dqblk)) {
169884d179dSJan Kara 		if (ret >= 0)
170884d179dSJan Kara 			ret = -EIO;
171884d179dSJan Kara 		goto out;
172884d179dSJan Kara 	}
173884d179dSJan Kara 	ret = 0;
174884d179dSJan Kara 	/* limits are stored as unsigned 32-bit data */
175b10a0819SJan Kara 	dqopt->info[type].dqi_max_spc_limit = 0xffffffffULL << QUOTABLOCK_BITS;
176b10a0819SJan Kara 	dqopt->info[type].dqi_max_ino_limit = 0xffffffff;
177268157baSJan Kara 	dqopt->info[type].dqi_igrace =
178268157baSJan Kara 			dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
179268157baSJan Kara 	dqopt->info[type].dqi_bgrace =
180268157baSJan Kara 			dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
181884d179dSJan Kara out:
18242fdb858SJan Kara 	up_read(&dqopt->dqio_sem);
183884d179dSJan Kara 	return ret;
184884d179dSJan Kara }
185884d179dSJan Kara 
v1_write_file_info(struct super_block * sb,int type)186884d179dSJan Kara static int v1_write_file_info(struct super_block *sb, int type)
187884d179dSJan Kara {
188884d179dSJan Kara 	struct quota_info *dqopt = sb_dqopt(sb);
189884d179dSJan Kara 	struct v1_disk_dqblk dqblk;
190884d179dSJan Kara 	int ret;
191884d179dSJan Kara 
1929a8ae30eSJan Kara 	down_write(&dqopt->dqio_sem);
193268157baSJan Kara 	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
194268157baSJan Kara 				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
195268157baSJan Kara 	if (ret != sizeof(struct v1_disk_dqblk)) {
196884d179dSJan Kara 		if (ret >= 0)
197884d179dSJan Kara 			ret = -EIO;
198884d179dSJan Kara 		goto out;
199884d179dSJan Kara 	}
20015512377SJan Kara 	spin_lock(&dq_data_lock);
20115512377SJan Kara 	dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
202884d179dSJan Kara 	dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
203884d179dSJan Kara 	dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
20415512377SJan Kara 	spin_unlock(&dq_data_lock);
205884d179dSJan Kara 	ret = sb->s_op->quota_write(sb, type, (char *)&dqblk,
206884d179dSJan Kara 	      sizeof(struct v1_disk_dqblk), v1_dqoff(0));
207884d179dSJan Kara 	if (ret == sizeof(struct v1_disk_dqblk))
208884d179dSJan Kara 		ret = 0;
209*f8107c99SYangtao Li 	else if (ret >= 0)
210884d179dSJan Kara 		ret = -EIO;
211884d179dSJan Kara out:
2129a8ae30eSJan Kara 	up_write(&dqopt->dqio_sem);
213884d179dSJan Kara 	return ret;
214884d179dSJan Kara }
215884d179dSJan Kara 
2161472da5fSAlexey Dobriyan static const struct quota_format_ops v1_format_ops = {
217884d179dSJan Kara 	.check_quota_file	= v1_check_quota_file,
218884d179dSJan Kara 	.read_file_info		= v1_read_file_info,
219884d179dSJan Kara 	.write_file_info	= v1_write_file_info,
220884d179dSJan Kara 	.read_dqblk		= v1_read_dqblk,
221884d179dSJan Kara 	.commit_dqblk		= v1_commit_dqblk,
222884d179dSJan Kara };
223884d179dSJan Kara 
224884d179dSJan Kara static struct quota_format_type v1_quota_format = {
225884d179dSJan Kara 	.qf_fmt_id	= QFMT_VFS_OLD,
226884d179dSJan Kara 	.qf_ops		= &v1_format_ops,
227884d179dSJan Kara 	.qf_owner	= THIS_MODULE
228884d179dSJan Kara };
229884d179dSJan Kara 
init_v1_quota_format(void)230884d179dSJan Kara static int __init init_v1_quota_format(void)
231884d179dSJan Kara {
232884d179dSJan Kara         return register_quota_format(&v1_quota_format);
233884d179dSJan Kara }
234884d179dSJan Kara 
exit_v1_quota_format(void)235884d179dSJan Kara static void __exit exit_v1_quota_format(void)
236884d179dSJan Kara {
237884d179dSJan Kara         unregister_quota_format(&v1_quota_format);
238884d179dSJan Kara }
239884d179dSJan Kara 
240884d179dSJan Kara module_init(init_v1_quota_format);
241884d179dSJan Kara module_exit(exit_v1_quota_format);
242884d179dSJan Kara 
243