2 * Implementation of new quotafile format
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
15 #include "quotaio_tree.h"
18 typedef char *dqbuf_t;
20 #define freedqbuf(buf) ext2fs_free_mem(&buf)
22 static inline dqbuf_t getdqbuf(void)
25 if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
26 log_err("Failed to allocate dqbuf");
33 /* Is given dquot empty? */
34 int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
38 for (i = 0; i < info->dqi_entry_size; i++)
44 int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
46 return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
50 static int get_index(qid_t id, int depth)
52 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
55 static inline void mark_quotafile_info_dirty(struct quota_handle *h)
57 h->qh_io_flags |= IOFL_INFODIRTY;
60 /* Read given block */
61 static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
65 err = h->e2fs_read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
68 log_err("Cannot read block %u: %s", blk, strerror(errno));
69 else if (err != QT_BLKSIZE)
70 memset(buf + err, 0, QT_BLKSIZE - err);
74 static int write_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
78 err = h->e2fs_write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
80 if (err < 0 && errno != ENOSPC)
81 log_err("Cannot write block (%u): %s", blk, strerror(errno));
82 if (err != QT_BLKSIZE)
87 /* Get free block in file (either from free list or create new one) */
88 static int get_free_dqblk(struct quota_handle *h)
90 dqbuf_t buf = getdqbuf();
91 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
92 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
98 if (info->dqi_free_blk) {
99 blk = info->dqi_free_blk;
100 read_blk(h, blk, buf);
101 info->dqi_free_blk = ext2fs_le32_to_cpu(dh->dqdh_next_free);
103 memset(buf, 0, QT_BLKSIZE);
104 /* Assure block allocation... */
105 if (write_blk(h, info->dqi_blocks, buf) < 0) {
107 log_err("Cannot allocate new quota block "
108 "(out of disk space).");
111 blk = info->dqi_blocks++;
113 mark_quotafile_info_dirty(h);
118 /* Put given block to free list */
119 static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
121 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
122 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
124 dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_blk);
125 dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
126 dh->dqdh_entries = ext2fs_cpu_to_le16(0);
127 info->dqi_free_blk = blk;
128 mark_quotafile_info_dirty(h);
129 write_blk(h, blk, buf);
132 /* Remove given block from the list of blocks with free entries */
133 static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
135 dqbuf_t tmpbuf = getdqbuf();
136 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
137 uint nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
139 ext2fs_le32_to_cpu(dh->dqdh_prev_free);
145 read_blk(h, nextblk, tmpbuf);
146 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
148 write_blk(h, nextblk, tmpbuf);
151 read_blk(h, prevblk, tmpbuf);
152 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
154 write_blk(h, prevblk, tmpbuf);
156 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
157 mark_quotafile_info_dirty(h);
160 dh->dqdh_next_free = dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
161 write_blk(h, blk, buf); /* No matter whether write succeeds
162 * block is out of list */
165 /* Insert given block to the beginning of list with free entries */
166 static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
168 dqbuf_t tmpbuf = getdqbuf();
169 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
170 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
175 dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_entry);
176 dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
177 write_blk(h, blk, buf);
178 if (info->dqi_free_entry) {
179 read_blk(h, info->dqi_free_entry, tmpbuf);
180 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
181 ext2fs_cpu_to_le32(blk);
182 write_blk(h, info->dqi_free_entry, tmpbuf);
185 info->dqi_free_entry = blk;
186 mark_quotafile_info_dirty(h);
189 /* Find space for dquot */
190 static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
194 struct qt_disk_dqdbheader *dh;
195 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
206 dh = (struct qt_disk_dqdbheader *)buf;
207 if (info->dqi_free_entry) {
208 blk = info->dqi_free_entry;
209 read_blk(h, blk, buf);
211 blk = get_free_dqblk(h);
217 memset(buf, 0, QT_BLKSIZE);
218 info->dqi_free_entry = blk;
219 mark_quotafile_info_dirty(h);
222 /* Block will be full? */
223 if (ext2fs_le16_to_cpu(dh->dqdh_entries) + 1 >=
224 qtree_dqstr_in_blk(info))
225 remove_free_dqentry(h, buf, blk);
228 ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) + 1);
229 /* Find free structure in block */
230 ddquot = buf + sizeof(struct qt_disk_dqdbheader);
232 i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
234 ddquot += info->dqi_entry_size;
236 if (i == qtree_dqstr_in_blk(info))
237 log_err("find_free_dqentry(): Data block full unexpectedly.");
239 write_blk(h, blk, buf);
240 dquot->dq_dqb.u.v2_mdqb.dqb_off =
241 (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
242 i * info->dqi_entry_size;
247 /* Insert reference to structure into the trie */
248 static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
249 uint * treeblk, int depth)
252 int newson = 0, newact = 0;
257 log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
263 ret = get_free_dqblk(h);
267 memset(buf, 0, QT_BLKSIZE);
270 read_blk(h, *treeblk, buf);
273 ref = (u_int32_t *) buf;
274 newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
277 if (depth == QT_TREEDEPTH - 1) {
279 log_err("Inserting already present quota entry "
281 ref[get_index(dquot->dq_id, depth)]);
282 newblk = find_free_dqentry(h, dquot, &ret);
284 ret = do_insert_tree(h, dquot, &newblk, depth + 1);
287 if (newson && ret >= 0) {
288 ref[get_index(dquot->dq_id, depth)] =
289 ext2fs_cpu_to_le32(newblk);
290 write_blk(h, *treeblk, buf);
291 } else if (newact && ret < 0) {
292 put_free_dqblk(h, buf, *treeblk);
300 /* Wrapper for inserting quota structure into tree */
301 static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
303 uint tmp = QT_TREEOFF;
305 if (do_insert_tree(h, dquot, &tmp, 0) < 0)
306 log_err("Cannot write quota (id %u): %s",
307 (uint) dquot->dq_id, strerror(errno));
310 /* Write dquot to file */
311 void qtree_write_dquot(struct dquot *dquot)
315 struct quota_handle *h = dquot->dq_h;
316 struct qtree_mem_dqinfo *info =
317 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
318 log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
319 dquot->dq_dqb.u.v2_mdqb.dqb_off,
320 info->dqi_entry_size);
321 ret = ext2fs_get_mem(info->dqi_entry_size, &ddquot);
324 log_err("Quota write failed (id %u): %s",
325 (uint)dquot->dq_id, strerror(errno));
329 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
330 dq_insert_tree(dquot->dq_h, dquot);
331 info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
332 log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
333 dquot->dq_dqb.u.v2_mdqb.dqb_off,
334 info->dqi_entry_size);
335 ret = h->e2fs_write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
336 info->dqi_entry_size);
338 if (ret != info->dqi_entry_size) {
341 log_err("Quota write failed (id %u): %s",
342 (uint)dquot->dq_id, strerror(errno));
344 ext2fs_free_mem(&ddquot);
347 /* Free dquot entry in data block */
348 static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
350 struct qt_disk_dqdbheader *dh;
351 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
352 dqbuf_t buf = getdqbuf();
357 if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
358 log_err("Quota structure has offset to other block (%u) "
359 "than it should (%u).", blk,
360 (uint) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
363 read_blk(h, blk, buf);
364 dh = (struct qt_disk_dqdbheader *)buf;
366 ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1);
368 if (!ext2fs_le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
369 remove_free_dqentry(h, buf, blk);
370 put_free_dqblk(h, buf, blk);
372 memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
373 ((1 << QT_BLKSIZE_BITS) - 1)),
374 0, info->dqi_entry_size);
376 /* First free entry? */
377 if (ext2fs_le16_to_cpu(dh->dqdh_entries) ==
378 qtree_dqstr_in_blk(info) - 1)
379 /* This will also write data block */
380 insert_free_dqentry(h, buf, blk);
382 write_blk(h, blk, buf);
384 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
388 /* Remove reference to dquot from tree */
389 static void remove_tree(struct quota_handle *h, struct dquot *dquot,
390 uint * blk, int depth)
392 dqbuf_t buf = getdqbuf();
394 u_int32_t *ref = (u_int32_t *) buf;
399 read_blk(h, *blk, buf);
400 newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
401 if (depth == QT_TREEDEPTH - 1) {
402 free_dqentry(h, dquot, newblk);
405 remove_tree(h, dquot, &newblk, depth + 1);
411 ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0);
413 /* Block got empty? */
414 for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
416 /* Don't put the root block into the free block list */
417 if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
418 put_free_dqblk(h, buf, *blk);
421 write_blk(h, *blk, buf);
427 /* Delete dquot from tree */
428 void qtree_delete_dquot(struct dquot *dquot)
430 uint tmp = QT_TREEOFF;
432 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
434 remove_tree(dquot->dq_h, dquot, &tmp, 0);
437 /* Find entry in block */
438 static ext2_loff_t find_block_dqentry(struct quota_handle *h,
439 struct dquot *dquot, uint blk)
441 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
442 dqbuf_t buf = getdqbuf();
444 char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
449 read_blk(h, blk, buf);
451 i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
453 ddquot += info->dqi_entry_size;
455 if (i == qtree_dqstr_in_blk(info))
456 log_err("Quota for id %u referenced but not present.",
459 return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
460 i * info->dqi_entry_size;
463 /* Find entry for given id in the tree */
464 static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
468 dqbuf_t buf = getdqbuf();
470 u_int32_t *ref = (u_int32_t *) buf;
475 read_blk(h, blk, buf);
477 blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
478 if (!blk) /* No reference? */
480 if (depth < QT_TREEDEPTH - 1)
481 ret = find_tree_dqentry(h, dquot, blk, depth + 1);
483 ret = find_block_dqentry(h, dquot, blk);
489 /* Find entry for given id in the tree - wrapper function */
490 static inline ext2_loff_t find_dqentry(struct quota_handle *h,
493 return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
497 * Read dquot from disk.
499 struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
501 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
505 struct dquot *dquot = get_empty_dquot();
509 if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) {
510 ext2fs_free_mem(&dquot);
516 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
517 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
519 offset = find_dqentry(h, dquot);
521 dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
522 ret = h->e2fs_read(&h->qh_qf, offset, ddquot,
523 info->dqi_entry_size);
524 if (ret != info->dqi_entry_size) {
527 log_err("Cannot read quota structure for id %u: %s",
528 dquot->dq_id, strerror(errno));
530 info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
532 ext2fs_free_mem(&ddquot);
537 * Scan all dquots in file and call callback on each
539 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
540 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
542 static int report_block(struct dquot *dquot, uint blk, char *bitmap,
543 int (*process_dquot) (struct dquot *, void *),
546 struct qtree_mem_dqinfo *info =
547 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
548 dqbuf_t buf = getdqbuf();
549 struct qt_disk_dqdbheader *dh;
556 set_bit(bitmap, blk);
557 read_blk(dquot->dq_h, blk, buf);
558 dh = (struct qt_disk_dqdbheader *)buf;
559 ddata = buf + sizeof(struct qt_disk_dqdbheader);
560 entries = ext2fs_le16_to_cpu(dh->dqdh_entries);
561 for (i = 0; i < qtree_dqstr_in_blk(info);
562 i++, ddata += info->dqi_entry_size)
563 if (!qtree_entry_unused(info, ddata)) {
564 dquot->dq_dqb.u.v2_mdqb.dqb_off =
565 (blk << QT_BLKSIZE_BITS) +
566 sizeof(struct qt_disk_dqdbheader) +
567 i * info->dqi_entry_size;
568 info->dqi_ops->disk2mem_dqblk(dquot, ddata);
569 if (process_dquot(dquot, data) < 0)
576 static void check_reference(struct quota_handle *h, uint blk)
578 if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
579 log_err("Illegal reference (%u >= %u) in %s quota file. "
580 "Quota file is probably corrupted.\n"
581 "Please run e2fsck (8) to fix it.",
583 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
584 type2name(h->qh_type));
587 static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
588 int (*process_dquot) (struct dquot *, void *),
592 dqbuf_t buf = getdqbuf();
593 u_int32_t *ref = (u_int32_t *) buf;
598 read_blk(dquot->dq_h, blk, buf);
599 if (depth == QT_TREEDEPTH - 1) {
600 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
601 blk = ext2fs_le32_to_cpu(ref[i]);
602 check_reference(dquot->dq_h, blk);
603 if (blk && !get_bit(bitmap, blk))
604 entries += report_block(dquot, blk, bitmap,
605 process_dquot, data);
608 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
609 blk = ext2fs_le32_to_cpu(ref[i]);
611 check_reference(dquot->dq_h, blk);
612 entries += report_tree(dquot, blk, depth + 1,
613 bitmap, process_dquot,
622 static uint find_set_bits(char *bmp, int blocks)
626 for (i = 0; i < blocks; i++)
632 int qtree_scan_dquots(struct quota_handle *h,
633 int (*process_dquot) (struct dquot *, void *),
637 struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
638 struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
639 struct dquot *dquot = get_empty_dquot();
645 if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) {
646 ext2fs_free_mem(&dquot);
649 v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap,
650 process_dquot, data);
651 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
652 ext2fs_free_mem(&bitmap);
653 ext2fs_free_mem(&dquot);