quotaio_v2.c revision 0ce0172984c807d3366e8db6e2a2b6ecae314832
1/*
2 * Implementation of new quotafile format
3 *
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
5 */
6
7#include "config.h"
8#include <sys/types.h>
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include "common.h"
16#include "quotaio_v2.h"
17#include "dqblk_v2.h"
18#include "quotaio.h"
19#include "quotaio_tree.h"
20
21static int v2_check_file(struct quota_handle *h, int type, int fmt);
22static int v2_init_io(struct quota_handle *h);
23static int v2_new_io(struct quota_handle *h);
24static int v2_write_info(struct quota_handle *h);
25static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id);
26static int v2_commit_dquot(struct dquot *dquot);
27static int v2_scan_dquots(struct quota_handle *h,
28			  int (*process_dquot) (struct dquot *dquot,
29						void *data),
30			  void *data);
31static int v2_report(struct quota_handle *h, int verbose);
32
33struct quotafile_ops quotafile_ops_2 = {
34	.check_file	= v2_check_file,
35	.init_io 	= v2_init_io,
36	.new_io 	= v2_new_io,
37	.write_info	= v2_write_info,
38	.read_dquot	= v2_read_dquot,
39	.commit_dquot	= v2_commit_dquot,
40	.scan_dquots	= v2_scan_dquots,
41	.report		= v2_report,
42};
43
44/*
45 * Copy dquot from disk to memory
46 */
47static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
48{
49	struct util_dqblk *m = &dquot->dq_dqb;
50	struct v2r1_disk_dqblk *d = dp, empty;
51
52	dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id);
53	m->dqb_ihardlimit = ext2fs_le64_to_cpu(d->dqb_ihardlimit);
54	m->dqb_isoftlimit = ext2fs_le64_to_cpu(d->dqb_isoftlimit);
55	m->dqb_bhardlimit = ext2fs_le64_to_cpu(d->dqb_bhardlimit);
56	m->dqb_bsoftlimit = ext2fs_le64_to_cpu(d->dqb_bsoftlimit);
57	m->dqb_curinodes = ext2fs_le64_to_cpu(d->dqb_curinodes);
58	m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace);
59	m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime);
60	m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime);
61
62	memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
63	empty.dqb_itime = ext2fs_cpu_to_le64(1);
64	if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
65		m->dqb_itime = 0;
66}
67
68/*
69 * Copy dquot from memory to disk
70 */
71static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot)
72{
73	struct util_dqblk *m = &dquot->dq_dqb;
74	struct v2r1_disk_dqblk *d = dp;
75
76	d->dqb_ihardlimit = ext2fs_cpu_to_le64(m->dqb_ihardlimit);
77	d->dqb_isoftlimit = ext2fs_cpu_to_le64(m->dqb_isoftlimit);
78	d->dqb_bhardlimit = ext2fs_cpu_to_le64(m->dqb_bhardlimit);
79	d->dqb_bsoftlimit = ext2fs_cpu_to_le64(m->dqb_bsoftlimit);
80	d->dqb_curinodes = ext2fs_cpu_to_le64(m->dqb_curinodes);
81	d->dqb_curspace = ext2fs_cpu_to_le64(m->dqb_curspace);
82	d->dqb_itime = ext2fs_cpu_to_le64(m->dqb_itime);
83	d->dqb_btime = ext2fs_cpu_to_le64(m->dqb_btime);
84	d->dqb_id = ext2fs_cpu_to_le32(dquot->dq_id);
85	if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
86		d->dqb_itime = ext2fs_cpu_to_le64(1);
87}
88
89static int v2r1_is_id(void *dp, struct dquot *dquot)
90{
91	struct v2r1_disk_dqblk *d = dp;
92	struct qtree_mem_dqinfo *info =
93			&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
94
95	if (qtree_entry_unused(info, dp))
96		return 0;
97	return ext2fs_le32_to_cpu(d->dqb_id) == dquot->dq_id;
98}
99
100static struct qtree_fmt_operations v2r1_fmt_ops = {
101	.mem2disk_dqblk = v2r1_mem2diskdqblk,
102	.disk2mem_dqblk = v2r1_disk2memdqblk,
103	.is_id = v2r1_is_id,
104};
105
106/*
107 * Copy dqinfo from disk to memory
108 */
109static inline void v2_disk2memdqinfo(struct util_dqinfo *m,
110				     struct v2_disk_dqinfo *d)
111{
112	m->dqi_bgrace = ext2fs_le32_to_cpu(d->dqi_bgrace);
113	m->dqi_igrace = ext2fs_le32_to_cpu(d->dqi_igrace);
114	m->u.v2_mdqi.dqi_flags = ext2fs_le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
115	m->u.v2_mdqi.dqi_qtree.dqi_blocks = ext2fs_le32_to_cpu(d->dqi_blocks);
116	m->u.v2_mdqi.dqi_qtree.dqi_free_blk =
117		ext2fs_le32_to_cpu(d->dqi_free_blk);
118	m->u.v2_mdqi.dqi_qtree.dqi_free_entry =
119				ext2fs_le32_to_cpu(d->dqi_free_entry);
120}
121
122/*
123 * Copy dqinfo from memory to disk
124 */
125static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d,
126				     struct util_dqinfo *m)
127{
128	d->dqi_bgrace = ext2fs_cpu_to_le32(m->dqi_bgrace);
129	d->dqi_igrace = ext2fs_cpu_to_le32(m->dqi_igrace);
130	d->dqi_flags = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
131	d->dqi_blocks = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
132	d->dqi_free_blk =
133		ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
134	d->dqi_free_entry =
135		ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
136}
137
138static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh)
139{
140	if (h->e2fs_read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) !=
141			sizeof(struct v2_disk_dqheader))
142		return 0;
143
144	return 1;
145}
146
147/*
148 * Check whether given quota file is in our format
149 */
150static int v2_check_file(struct quota_handle *h, int type, int fmt)
151{
152	struct v2_disk_dqheader dqh;
153	int file_magics[] = INITQMAGICS;
154
155	if (fmt != QFMT_VFS_V1)
156		return 0;
157
158	if (!v2_read_header(h, &dqh))
159		return 0;
160
161	if (ext2fs_le32_to_cpu(dqh.dqh_magic) != file_magics[type]) {
162		if (ext2fs_be32_to_cpu(dqh.dqh_magic) == file_magics[type])
163			log_err("Your quota file is stored in wrong endianity");
164		return 0;
165	}
166	if (V2_VERSION != ext2fs_le32_to_cpu(dqh.dqh_version))
167		return 0;
168	return 1;
169}
170
171/*
172 * Open quotafile
173 */
174static int v2_init_io(struct quota_handle *h)
175{
176	struct v2_disk_dqinfo ddqinfo;
177
178	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
179		sizeof(struct v2r1_disk_dqblk);
180	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
181
182	/* Read information about quotafile */
183	if (h->e2fs_read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
184			 sizeof(ddqinfo)) != sizeof(ddqinfo))
185		return -1;
186	v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
187	return 0;
188}
189
190/*
191 * Initialize new quotafile
192 */
193static int v2_new_io(struct quota_handle *h)
194{
195	int file_magics[] = INITQMAGICS;
196	struct v2_disk_dqheader ddqheader;
197	struct v2_disk_dqinfo ddqinfo;
198
199	if (h->qh_fmt != QFMT_VFS_V1)
200		return -1;
201
202	/* Write basic quota header */
203	ddqheader.dqh_magic = ext2fs_cpu_to_le32(file_magics[h->qh_type]);
204	ddqheader.dqh_version = ext2fs_cpu_to_le32(V2_VERSION);
205	if (h->e2fs_write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) !=
206			sizeof(ddqheader))
207		return -1;
208
209	/* Write information about quotafile */
210	h->qh_info.dqi_bgrace = MAX_DQ_TIME;
211	h->qh_info.dqi_igrace = MAX_IQ_TIME;
212	h->qh_info.u.v2_mdqi.dqi_flags = 0;
213	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1;
214	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0;
215	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0;
216	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
217				sizeof(struct v2r1_disk_dqblk);
218	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
219	v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
220	if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
221			  sizeof(ddqinfo)) !=
222	    sizeof(ddqinfo))
223		return -1;
224
225	return 0;
226}
227
228/*
229 * Write information (grace times to file)
230 */
231static int v2_write_info(struct quota_handle *h)
232{
233	struct v2_disk_dqinfo ddqinfo;
234
235	v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
236	if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) !=
237			sizeof(ddqinfo))
238		return -1;
239
240	return 0;
241}
242
243/*
244 * Read dquot from disk
245 */
246static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id)
247{
248	return qtree_read_dquot(h, id);
249}
250
251/*
252 * Commit changes of dquot to disk - it might also mean deleting it when quota
253 * became fake one and user has no blocks.
254 * User can process use 'errno' to detect errstr.
255 */
256static int v2_commit_dquot(struct dquot *dquot)
257{
258	struct util_dqblk *b = &dquot->dq_dqb;
259
260	if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit &&
261	    !b->dqb_isoftlimit && !b->dqb_bhardlimit && !b->dqb_ihardlimit)
262		qtree_delete_dquot(dquot);
263	else
264		qtree_write_dquot(dquot);
265	return 0;
266}
267
268static int v2_scan_dquots(struct quota_handle *h,
269			  int (*process_dquot) (struct dquot *, void *),
270			  void *data)
271{
272	return qtree_scan_dquots(h, process_dquot, data);
273}
274
275/* Report information about quotafile.
276 * TODO: Not used right now, but we should be able to use this when we add
277 * support to debugfs to read quota files.
278 */
279static int v2_report(struct quota_handle *h, int verbose)
280{
281	log_err("Not Implemented.");
282	return -1;
283}
284