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