Linux Audio

Check our new training course

Embedded Linux training

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