2 * Implementation of new quotafile format
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
15 #include <asm/byteorder.h>
19 #include "quotaio_v2.h"
23 #include "quotaio_generic.h"
25 typedef char *dqbuf_t;
27 static int v2_check_file(int fd, int type, int fmt);
28 static int v2_init_io(struct quota_handle *h);
29 static int v2_new_io(struct quota_handle *h);
30 static int v2_write_info(struct quota_handle *h);
31 static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id);
32 static int v2_commit_dquot(struct dquot *dquot, int flags);
33 static int v2_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *dquot, char *dqname));
34 static int v2_report(struct quota_handle *h, int verbose);
36 struct quotafile_ops quotafile_ops_2 = {
37 check_file: v2_check_file,
40 write_info: v2_write_info,
41 read_dquot: v2_read_dquot,
42 commit_dquot: v2_commit_dquot,
43 scan_dquots: v2_scan_dquots,
47 #define getdqbuf() smalloc(V2_DQBLKSIZE)
48 #define freedqbuf(buf) free(buf)
51 * Copy dquot from disk to memory
53 static void v2r0_disk2memdqblk(struct dquot *dquot, void *dp)
55 struct util_dqblk *m = &dquot->dq_dqb;
56 struct v2r0_disk_dqblk *d = dp, empty;
58 dquot->dq_id = __le32_to_cpu(d->dqb_id);
59 m->dqb_ihardlimit = __le32_to_cpu(d->dqb_ihardlimit);
60 m->dqb_isoftlimit = __le32_to_cpu(d->dqb_isoftlimit);
61 m->dqb_bhardlimit = __le32_to_cpu(d->dqb_bhardlimit);
62 m->dqb_bsoftlimit = __le32_to_cpu(d->dqb_bsoftlimit);
63 m->dqb_curinodes = __le32_to_cpu(d->dqb_curinodes);
64 m->dqb_curspace = __le64_to_cpu(d->dqb_curspace);
65 m->dqb_itime = __le64_to_cpu(d->dqb_itime);
66 m->dqb_btime = __le64_to_cpu(d->dqb_btime);
68 memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
69 empty.dqb_itime = __cpu_to_le64(1);
70 if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
75 * Copy dquot from memory to disk
77 static void v2r0_mem2diskdqblk(void *dp, struct dquot *dquot)
79 struct util_dqblk *m = &dquot->dq_dqb;
80 struct v2r0_disk_dqblk *d = dp;
81 struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
83 d->dqb_ihardlimit = __cpu_to_le32(m->dqb_ihardlimit);
84 d->dqb_isoftlimit = __cpu_to_le32(m->dqb_isoftlimit);
85 d->dqb_bhardlimit = __cpu_to_le32(m->dqb_bhardlimit);
86 d->dqb_bsoftlimit = __cpu_to_le32(m->dqb_bsoftlimit);
87 d->dqb_curinodes = __cpu_to_le32(m->dqb_curinodes);
88 d->dqb_curspace = __cpu_to_le64(m->dqb_curspace);
89 d->dqb_itime = __cpu_to_le64(m->dqb_itime);
90 d->dqb_btime = __cpu_to_le64(m->dqb_btime);
91 d->dqb_id = __cpu_to_le32(dquot->dq_id);
92 if (qtree_entry_unused(info, dp))
93 d->dqb_itime = __cpu_to_le64(1);
96 static int v2r0_is_id(void *dp, struct dquot *dquot)
98 struct v2r0_disk_dqblk *d = dp;
99 struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
101 if (qtree_entry_unused(info, dp))
103 return __le32_to_cpu(d->dqb_id) == dquot->dq_id;
107 * Copy dquot from disk to memory
109 static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
111 struct util_dqblk *m = &dquot->dq_dqb;
112 struct v2r1_disk_dqblk *d = dp, empty;
114 dquot->dq_id = __le32_to_cpu(d->dqb_id);
115 m->dqb_ihardlimit = __le64_to_cpu(d->dqb_ihardlimit);
116 m->dqb_isoftlimit = __le64_to_cpu(d->dqb_isoftlimit);
117 m->dqb_bhardlimit = __le64_to_cpu(d->dqb_bhardlimit);
118 m->dqb_bsoftlimit = __le64_to_cpu(d->dqb_bsoftlimit);
119 m->dqb_curinodes = __le64_to_cpu(d->dqb_curinodes);
120 m->dqb_curspace = __le64_to_cpu(d->dqb_curspace);
121 m->dqb_itime = __le64_to_cpu(d->dqb_itime);
122 m->dqb_btime = __le64_to_cpu(d->dqb_btime);
124 memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
125 empty.dqb_itime = __cpu_to_le64(1);
126 if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
131 * Copy dquot from memory to disk
133 static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot)
135 struct util_dqblk *m = &dquot->dq_dqb;
136 struct v2r1_disk_dqblk *d = dp;
138 d->dqb_ihardlimit = __cpu_to_le64(m->dqb_ihardlimit);
139 d->dqb_isoftlimit = __cpu_to_le64(m->dqb_isoftlimit);
140 d->dqb_bhardlimit = __cpu_to_le64(m->dqb_bhardlimit);
141 d->dqb_bsoftlimit = __cpu_to_le64(m->dqb_bsoftlimit);
142 d->dqb_curinodes = __cpu_to_le64(m->dqb_curinodes);
143 d->dqb_curspace = __cpu_to_le64(m->dqb_curspace);
144 d->dqb_itime = __cpu_to_le64(m->dqb_itime);
145 d->dqb_btime = __cpu_to_le64(m->dqb_btime);
146 d->dqb_id = __cpu_to_le32(dquot->dq_id);
147 if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
148 d->dqb_itime = __cpu_to_le64(1);
151 static int v2r1_is_id(void *dp, struct dquot *dquot)
153 struct v2r1_disk_dqblk *d = dp;
154 struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
156 if (qtree_entry_unused(info, dp))
158 return __le32_to_cpu(d->dqb_id) == dquot->dq_id;
161 static struct qtree_fmt_operations v2r0_fmt_ops = {
162 .mem2disk_dqblk = v2r0_mem2diskdqblk,
163 .disk2mem_dqblk = v2r0_disk2memdqblk,
167 static struct qtree_fmt_operations v2r1_fmt_ops = {
168 .mem2disk_dqblk = v2r1_mem2diskdqblk,
169 .disk2mem_dqblk = v2r1_disk2memdqblk,
174 * Copy dqinfo from disk to memory
176 static inline void v2_disk2memdqinfo(struct util_dqinfo *m, struct v2_disk_dqinfo *d)
178 m->dqi_bgrace = __le32_to_cpu(d->dqi_bgrace);
179 m->dqi_igrace = __le32_to_cpu(d->dqi_igrace);
180 m->u.v2_mdqi.dqi_flags = __le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
181 m->u.v2_mdqi.dqi_qtree.dqi_blocks = __le32_to_cpu(d->dqi_blocks);
182 m->u.v2_mdqi.dqi_qtree.dqi_free_blk = __le32_to_cpu(d->dqi_free_blk);
183 m->u.v2_mdqi.dqi_qtree.dqi_free_entry = __le32_to_cpu(d->dqi_free_entry);
187 * Copy dqinfo from memory to disk
189 static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d, struct util_dqinfo *m)
191 d->dqi_bgrace = __cpu_to_le32(m->dqi_bgrace);
192 d->dqi_igrace = __cpu_to_le32(m->dqi_igrace);
193 d->dqi_flags = __cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
194 d->dqi_blocks = __cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
195 d->dqi_free_blk = __cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
196 d->dqi_free_entry = __cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
199 /* Convert kernel quotablock format to utility one */
200 static inline void v2_kern2utildqblk(struct util_dqblk *u, struct v2_kern_dqblk *k)
202 u->dqb_ihardlimit = k->dqb_ihardlimit;
203 u->dqb_isoftlimit = k->dqb_isoftlimit;
204 u->dqb_bhardlimit = k->dqb_bhardlimit;
205 u->dqb_bsoftlimit = k->dqb_bsoftlimit;
206 u->dqb_curinodes = k->dqb_curinodes;
207 u->dqb_curspace = k->dqb_curspace;
208 u->dqb_itime = k->dqb_itime;
209 u->dqb_btime = k->dqb_btime;
212 /* Convert utility quotablock format to kernel one */
213 static inline void v2_util2kerndqblk(struct v2_kern_dqblk *k, struct util_dqblk *u)
215 k->dqb_ihardlimit = u->dqb_ihardlimit;
216 k->dqb_isoftlimit = u->dqb_isoftlimit;
217 k->dqb_bhardlimit = u->dqb_bhardlimit;
218 k->dqb_bsoftlimit = u->dqb_bsoftlimit;
219 k->dqb_curinodes = u->dqb_curinodes;
220 k->dqb_curspace = u->dqb_curspace;
221 k->dqb_itime = u->dqb_itime;
222 k->dqb_btime = u->dqb_btime;
225 static int v2_read_header(int fd, struct v2_disk_dqheader *h)
227 lseek(fd, 0, SEEK_SET);
228 if (read(fd, h, sizeof(struct v2_disk_dqheader)) != sizeof(struct v2_disk_dqheader))
234 * Check whether given quota file is in our format
236 static int v2_check_file(int fd, int type, int fmt)
238 struct v2_disk_dqheader h;
239 int file_magics[] = INITQMAGICS;
240 int known_versions[] = INIT_V2_VERSIONS;
243 if (!v2_read_header(fd, &h))
247 else if (fmt == QF_VFSV1)
252 if (__le32_to_cpu(h.dqh_magic) != file_magics[type]) {
253 if (__be32_to_cpu(h.dqh_magic) == file_magics[type])
254 die(3, _("Your quota file is stored in wrong endianity. Please use convertquota(8) to convert it.\n"));
257 if (__le32_to_cpu(h.dqh_version) > known_versions[type])
259 if (version != __le32_to_cpu(h.dqh_version))
267 static int v2_init_io(struct quota_handle *h)
269 if (QIO_ENABLED(h)) {
270 if (kernel_iface == IFACE_GENERIC) {
271 if (vfs_get_info(h) < 0)
275 struct v2_kern_dqinfo kdqinfo;
277 if (quotactl(QCMD(Q_V2_GETINFO, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0) {
278 /* Temporary check just before fix gets to kernel */
279 if (errno == EPERM) /* Don't have permission to get information? */
283 h->qh_info.dqi_bgrace = kdqinfo.dqi_bgrace;
284 h->qh_info.dqi_igrace = kdqinfo.dqi_igrace;
285 h->qh_info.u.v2_mdqi.dqi_flags = kdqinfo.dqi_flags;
286 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = kdqinfo.dqi_blocks;
287 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = kdqinfo.dqi_free_blk;
288 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = kdqinfo.dqi_free_entry;
291 if (h->qh_fd != -1) {
292 struct v2_disk_dqinfo ddqinfo;
293 struct v2_disk_dqheader header;
295 if (!v2_read_header(h->qh_fd, &header))
298 lseek(h->qh_fd, V2_DQINFOOFF, SEEK_SET);
299 if (read(h->qh_fd, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo))
301 /* Convert everything */
303 v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
304 else /* We need just the number of blocks */
305 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = __le32_to_cpu(ddqinfo.dqi_blocks);
307 if (__le32_to_cpu(header.dqh_version) == 0) {
308 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
309 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r0_fmt_ops;
311 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
312 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
315 /* We don't have the file open -> we don't need quota tree operations */
316 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = NULL;
322 * Initialize new quotafile
324 static int v2_new_io(struct quota_handle *h)
326 int file_magics[] = INITQMAGICS;
327 struct v2_disk_dqheader ddqheader;
328 struct v2_disk_dqinfo ddqinfo;
331 if (h->qh_fmt == QF_VFSV0)
333 else if (h->qh_fmt == QF_VFSV1)
338 /* Write basic quota header */
339 ddqheader.dqh_magic = __cpu_to_le32(file_magics[h->qh_type]);
340 ddqheader.dqh_version = __cpu_to_le32(version);
341 lseek(h->qh_fd, 0, SEEK_SET);
342 if (write(h->qh_fd, &ddqheader, sizeof(ddqheader)) != sizeof(ddqheader))
344 /* Write information about quotafile */
345 h->qh_info.dqi_bgrace = MAX_DQ_TIME;
346 h->qh_info.dqi_igrace = MAX_IQ_TIME;
347 h->qh_info.u.v2_mdqi.dqi_flags = 0;
348 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1;
349 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0;
350 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0;
352 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
353 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r0_fmt_ops;
354 } else if (version == 1) {
355 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
356 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
358 v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
359 lseek(h->qh_fd, V2_DQINFOOFF, SEEK_SET);
360 if (write(h->qh_fd, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo))
366 * Write information (grace times to file)
368 static int v2_write_info(struct quota_handle *h)
371 errstr(_("Trying to write info to readonly quotafile on %s\n"), h->qh_quotadev);
375 if (QIO_ENABLED(h)) {
376 if (kernel_iface == IFACE_GENERIC) {
377 if (vfs_set_info(h, IIF_BGRACE | IIF_IGRACE))
381 struct v2_kern_dqinfo kdqinfo;
383 kdqinfo.dqi_bgrace = h->qh_info.dqi_bgrace;
384 kdqinfo.dqi_igrace = h->qh_info.dqi_igrace;
385 kdqinfo.dqi_flags = h->qh_info.u.v2_mdqi.dqi_flags;
386 kdqinfo.dqi_blocks = h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks;
387 kdqinfo.dqi_free_blk = h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk;
388 kdqinfo.dqi_free_entry = h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry;
389 if (quotactl(QCMD(Q_V2_SETGRACE, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0 ||
390 quotactl(QCMD(Q_V2_SETFLAGS, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0)
395 struct v2_disk_dqinfo ddqinfo;
397 v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
398 lseek(h->qh_fd, V2_DQINFOOFF, SEEK_SET);
399 if (write(h->qh_fd, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo))
406 * Read dquot (either from disk or from kernel)
407 * User can use errno to detect errstr when NULL is returned
409 static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id)
411 if (QIO_ENABLED(h)) {
412 struct dquot *dquot = get_empty_dquot();
416 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
417 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
418 if (kernel_iface == IFACE_GENERIC) {
419 if (vfs_get_dquot(dquot) < 0) {
425 struct v2_kern_dqblk kdqblk;
427 if (quotactl(QCMD(Q_V2_GETQUOTA, h->qh_type), h->qh_quotadev, id, (void *)&kdqblk) < 0) {
431 v2_kern2utildqblk(&dquot->dq_dqb, &kdqblk);
435 return qtree_read_dquot(h, id);
439 * Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks...
440 * User can process use 'errno' to detect errstr
442 static int v2_commit_dquot(struct dquot *dquot, int flags)
444 struct util_dqblk *b = &dquot->dq_dqb;
446 if (QIO_RO(dquot->dq_h)) {
447 errstr(_("Trying to write quota to readonly quotafile on %s\n"), dquot->dq_h->qh_quotadev);
451 if (QIO_ENABLED(dquot->dq_h)) {
452 if (kernel_iface == IFACE_GENERIC) {
453 if (vfs_set_dquot(dquot, flags) < 0)
457 struct v2_kern_dqblk kdqblk;
460 if (flags == COMMIT_USAGE)
462 else if (flags == COMMIT_LIMITS)
464 else if (flags & COMMIT_TIMES) {
470 v2_util2kerndqblk(&kdqblk, &dquot->dq_dqb);
471 if (quotactl(QCMD(cmd, dquot->dq_h->qh_type), dquot->dq_h->qh_quotadev,
472 dquot->dq_id, (void *)&kdqblk) < 0)
477 if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit && !b->dqb_isoftlimit
478 && !b->dqb_bhardlimit && !b->dqb_ihardlimit)
479 qtree_delete_dquot(dquot);
481 qtree_write_dquot(dquot);
485 static int v2_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, char *))
487 if (QIO_ENABLED(h)) /* Kernel uses same file? */
488 if (quotactl(QCMD((kernel_iface == IFACE_GENERIC) ? Q_SYNC : Q_6_5_SYNC, h->qh_type),
489 h->qh_quotadev, 0, NULL) < 0)
490 die(4, _("Cannot sync quotas on device %s: %s\n"), h->qh_quotadev,
492 return qtree_scan_dquots(h, process_dquot);
495 /* Report information about quotafile */
496 static int v2_report(struct quota_handle *h, int verbose)
499 struct v2_mem_dqinfo *info = &h->qh_info.u.v2_mdqi;
501 printf(_("Statistics:\nTotal blocks: %u\nData blocks: %u\nEntries: %u\nUsed average: %f\n"),
502 info->dqi_qtree.dqi_blocks, info->dqi_data_blocks, info->dqi_used_entries,
503 ((float)info->dqi_used_entries) / info->dqi_data_blocks);