1 /*
2 * Implementation of new quotafile format
3 *
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
5 */
6
7 #include <sys/types.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "common.h"
15 #include "quotaio_tree.h"
16 #include "quotaio.h"
17
18 typedef char *dqbuf_t;
19
20 #define freedqbuf(buf) ext2fs_free_mem(&buf)
21
getdqbuf(void)22 static inline dqbuf_t getdqbuf(void)
23 {
24 dqbuf_t buf;
25 if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
26 log_err("Failed to allocate dqbuf");
27 return NULL;
28 }
29
30 return buf;
31 }
32
33 /* Is given dquot empty? */
qtree_entry_unused(struct qtree_mem_dqinfo * info,char * disk)34 int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
35 {
36 int i;
37
38 for (i = 0; i < info->dqi_entry_size; i++)
39 if (disk[i])
40 return 0;
41 return 1;
42 }
43
qtree_dqstr_in_blk(struct qtree_mem_dqinfo * info)44 int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
45 {
46 return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
47 info->dqi_entry_size;
48 }
49
get_index(qid_t id,int depth)50 static int get_index(qid_t id, int depth)
51 {
52 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
53 }
54
mark_quotafile_info_dirty(struct quota_handle * h)55 static inline void mark_quotafile_info_dirty(struct quota_handle *h)
56 {
57 h->qh_io_flags |= IOFL_INFODIRTY;
58 }
59
60 /* Read given block */
read_blk(struct quota_handle * h,uint blk,dqbuf_t buf)61 static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
62 {
63 int err;
64
65 err = h->e2fs_read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
66 QT_BLKSIZE);
67 if (err < 0)
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);
71 }
72
73 /* Write block */
write_blk(struct quota_handle * h,uint blk,dqbuf_t buf)74 static int write_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
75 {
76 int err;
77
78 err = h->e2fs_write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
79 QT_BLKSIZE);
80 if (err < 0 && errno != ENOSPC)
81 log_err("Cannot write block (%u): %s", blk, strerror(errno));
82 if (err != QT_BLKSIZE)
83 return -ENOSPC;
84 return 0;
85 }
86
87 /* Get free block in file (either from free list or create new one) */
get_free_dqblk(struct quota_handle * h)88 static int get_free_dqblk(struct quota_handle *h)
89 {
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;
93 int blk;
94
95 if (!buf)
96 return -ENOMEM;
97
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);
102 } else {
103 memset(buf, 0, QT_BLKSIZE);
104 /* Assure block allocation... */
105 if (write_blk(h, info->dqi_blocks, buf) < 0) {
106 freedqbuf(buf);
107 log_err("Cannot allocate new quota block "
108 "(out of disk space).");
109 return -ENOSPC;
110 }
111 blk = info->dqi_blocks++;
112 }
113 mark_quotafile_info_dirty(h);
114 freedqbuf(buf);
115 return blk;
116 }
117
118 /* Put given block to free list */
put_free_dqblk(struct quota_handle * h,dqbuf_t buf,uint blk)119 static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
120 {
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;
123
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);
130 }
131
132 /* Remove given block from the list of blocks with free entries */
remove_free_dqentry(struct quota_handle * h,dqbuf_t buf,uint blk)133 static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
134 {
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 =
138
139 ext2fs_le32_to_cpu(dh->dqdh_prev_free);
140
141 if (!tmpbuf)
142 return;
143
144 if (nextblk) {
145 read_blk(h, nextblk, tmpbuf);
146 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
147 dh->dqdh_prev_free;
148 write_blk(h, nextblk, tmpbuf);
149 }
150 if (prevblk) {
151 read_blk(h, prevblk, tmpbuf);
152 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
153 dh->dqdh_next_free;
154 write_blk(h, prevblk, tmpbuf);
155 } else {
156 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
157 mark_quotafile_info_dirty(h);
158 }
159 freedqbuf(tmpbuf);
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 */
163 }
164
165 /* Insert given block to the beginning of list with free entries */
insert_free_dqentry(struct quota_handle * h,dqbuf_t buf,uint blk)166 static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
167 {
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;
171
172 if (!tmpbuf)
173 return;
174
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);
183 }
184 freedqbuf(tmpbuf);
185 info->dqi_free_entry = blk;
186 mark_quotafile_info_dirty(h);
187 }
188
189 /* Find space for dquot */
find_free_dqentry(struct quota_handle * h,struct dquot * dquot,int * err)190 static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
191 int *err)
192 {
193 int blk, i;
194 struct qt_disk_dqdbheader *dh;
195 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
196 char *ddquot;
197 dqbuf_t buf;
198
199 *err = 0;
200 buf = getdqbuf();
201 if (!buf) {
202 *err = -ENOMEM;
203 return 0;
204 }
205
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);
210 } else {
211 blk = get_free_dqblk(h);
212 if (blk < 0) {
213 freedqbuf(buf);
214 *err = blk;
215 return 0;
216 }
217 memset(buf, 0, QT_BLKSIZE);
218 info->dqi_free_entry = blk;
219 mark_quotafile_info_dirty(h);
220 }
221
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);
226
227 dh->dqdh_entries =
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);
231 for (i = 0;
232 i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
233 i++)
234 ddquot += info->dqi_entry_size;
235
236 if (i == qtree_dqstr_in_blk(info))
237 log_err("find_free_dqentry(): Data block full unexpectedly.");
238
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;
243 freedqbuf(buf);
244 return blk;
245 }
246
247 /* Insert reference to structure into the trie */
do_insert_tree(struct quota_handle * h,struct dquot * dquot,uint * treeblk,int depth)248 static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
249 uint * treeblk, int depth)
250 {
251 dqbuf_t buf;
252 int newson = 0, newact = 0;
253 u_int32_t *ref;
254 uint newblk;
255 int ret = 0;
256
257 log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
258 buf = getdqbuf();
259 if (!buf)
260 return -ENOMEM;
261
262 if (!*treeblk) {
263 ret = get_free_dqblk(h);
264 if (ret < 0)
265 goto out_buf;
266 *treeblk = ret;
267 memset(buf, 0, QT_BLKSIZE);
268 newact = 1;
269 } else {
270 read_blk(h, *treeblk, buf);
271 }
272
273 ref = (u_int32_t *) buf;
274 newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
275 if (!newblk)
276 newson = 1;
277 if (depth == QT_TREEDEPTH - 1) {
278 if (newblk)
279 log_err("Inserting already present quota entry "
280 "(block %u).",
281 ref[get_index(dquot->dq_id, depth)]);
282 newblk = find_free_dqentry(h, dquot, &ret);
283 } else {
284 ret = do_insert_tree(h, dquot, &newblk, depth + 1);
285 }
286
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);
293 }
294
295 out_buf:
296 freedqbuf(buf);
297 return ret;
298 }
299
300 /* Wrapper for inserting quota structure into tree */
dq_insert_tree(struct quota_handle * h,struct dquot * dquot)301 static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
302 {
303 uint tmp = QT_TREEOFF;
304
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));
308 }
309
310 /* Write dquot to file */
qtree_write_dquot(struct dquot * dquot)311 void qtree_write_dquot(struct dquot *dquot)
312 {
313 ssize_t ret;
314 char *ddquot;
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);
322 if (ret) {
323 errno = ENOMEM;
324 log_err("Quota write failed (id %u): %s",
325 (uint)dquot->dq_id, strerror(errno));
326 return;
327 }
328
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);
337
338 if (ret != info->dqi_entry_size) {
339 if (ret > 0)
340 errno = ENOSPC;
341 log_err("Quota write failed (id %u): %s",
342 (uint)dquot->dq_id, strerror(errno));
343 }
344 ext2fs_free_mem(&ddquot);
345 }
346
347 /* Free dquot entry in data block */
free_dqentry(struct quota_handle * h,struct dquot * dquot,uint blk)348 static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
349 {
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();
353
354 if (!buf)
355 return;
356
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 >>
361 QT_BLKSIZE_BITS));
362
363 read_blk(h, blk, buf);
364 dh = (struct qt_disk_dqdbheader *)buf;
365 dh->dqdh_entries =
366 ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1);
367
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);
371 } else {
372 memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
373 ((1 << QT_BLKSIZE_BITS) - 1)),
374 0, info->dqi_entry_size);
375
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);
381 else
382 write_blk(h, blk, buf);
383 }
384 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
385 freedqbuf(buf);
386 }
387
388 /* Remove reference to dquot from tree */
remove_tree(struct quota_handle * h,struct dquot * dquot,uint * blk,int depth)389 static void remove_tree(struct quota_handle *h, struct dquot *dquot,
390 uint * blk, int depth)
391 {
392 dqbuf_t buf = getdqbuf();
393 uint newblk;
394 u_int32_t *ref = (u_int32_t *) buf;
395
396 if (!buf)
397 return;
398
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);
403 newblk = 0;
404 } else {
405 remove_tree(h, dquot, &newblk, depth + 1);
406 }
407
408 if (!newblk) {
409 int i;
410
411 ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0);
412
413 /* Block got empty? */
414 for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
415
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);
419 *blk = 0;
420 } else {
421 write_blk(h, *blk, buf);
422 }
423 }
424 freedqbuf(buf);
425 }
426
427 /* Delete dquot from tree */
qtree_delete_dquot(struct dquot * dquot)428 void qtree_delete_dquot(struct dquot *dquot)
429 {
430 uint tmp = QT_TREEOFF;
431
432 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
433 return;
434 remove_tree(dquot->dq_h, dquot, &tmp, 0);
435 }
436
437 /* Find entry in block */
find_block_dqentry(struct quota_handle * h,struct dquot * dquot,uint blk)438 static ext2_loff_t find_block_dqentry(struct quota_handle *h,
439 struct dquot *dquot, uint blk)
440 {
441 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
442 dqbuf_t buf = getdqbuf();
443 int i;
444 char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
445
446 if (!buf)
447 return -ENOMEM;
448
449 read_blk(h, blk, buf);
450 for (i = 0;
451 i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
452 i++)
453 ddquot += info->dqi_entry_size;
454
455 if (i == qtree_dqstr_in_blk(info))
456 log_err("Quota for id %u referenced but not present.",
457 dquot->dq_id);
458 freedqbuf(buf);
459 return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
460 i * info->dqi_entry_size;
461 }
462
463 /* Find entry for given id in the tree */
find_tree_dqentry(struct quota_handle * h,struct dquot * dquot,uint blk,int depth)464 static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
465 struct dquot *dquot,
466 uint blk, int depth)
467 {
468 dqbuf_t buf = getdqbuf();
469 ext2_loff_t ret = 0;
470 u_int32_t *ref = (u_int32_t *) buf;
471
472 if (!buf)
473 return -ENOMEM;
474
475 read_blk(h, blk, buf);
476 ret = 0;
477 blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
478 if (!blk) /* No reference? */
479 goto out_buf;
480 if (depth < QT_TREEDEPTH - 1)
481 ret = find_tree_dqentry(h, dquot, blk, depth + 1);
482 else
483 ret = find_block_dqentry(h, dquot, blk);
484 out_buf:
485 freedqbuf(buf);
486 return ret;
487 }
488
489 /* Find entry for given id in the tree - wrapper function */
find_dqentry(struct quota_handle * h,struct dquot * dquot)490 static inline ext2_loff_t find_dqentry(struct quota_handle *h,
491 struct dquot *dquot)
492 {
493 return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
494 }
495
496 /*
497 * Read dquot from disk.
498 */
qtree_read_dquot(struct quota_handle * h,qid_t id)499 struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
500 {
501 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
502 ext2_loff_t offset;
503 ssize_t ret;
504 char *ddquot;
505 struct dquot *dquot = get_empty_dquot();
506
507 if (!dquot)
508 return NULL;
509 if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) {
510 ext2fs_free_mem(&dquot);
511 return NULL;
512 }
513
514 dquot->dq_id = id;
515 dquot->dq_h = h;
516 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
517 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
518
519 offset = find_dqentry(h, dquot);
520 if (offset > 0) {
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) {
525 if (ret > 0)
526 errno = EIO;
527 log_err("Cannot read quota structure for id %u: %s",
528 dquot->dq_id, strerror(errno));
529 }
530 info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
531 }
532 ext2fs_free_mem(&ddquot);
533 return dquot;
534 }
535
536 /*
537 * Scan all dquots in file and call callback on each
538 */
539 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
540 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
541
report_block(struct dquot * dquot,uint blk,char * bitmap,int (* process_dquot)(struct dquot *,void *),void * data)542 static int report_block(struct dquot *dquot, uint blk, char *bitmap,
543 int (*process_dquot) (struct dquot *, void *),
544 void *data)
545 {
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;
550 char *ddata;
551 int entries, i;
552
553 if (!buf)
554 return 0;
555
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)
570 break;
571 }
572 freedqbuf(buf);
573 return entries;
574 }
575
check_reference(struct quota_handle * h,uint blk)576 static void check_reference(struct quota_handle *h, uint blk)
577 {
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.",
582 blk,
583 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
584 type2name(h->qh_type));
585 }
586
report_tree(struct dquot * dquot,uint blk,int depth,char * bitmap,int (* process_dquot)(struct dquot *,void *),void * data)587 static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
588 int (*process_dquot) (struct dquot *, void *),
589 void *data)
590 {
591 int entries = 0, i;
592 dqbuf_t buf = getdqbuf();
593 u_int32_t *ref = (u_int32_t *) buf;
594
595 if (!buf)
596 return 0;
597
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);
606 }
607 } else {
608 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
609 blk = ext2fs_le32_to_cpu(ref[i]);
610 if (blk) {
611 check_reference(dquot->dq_h, blk);
612 entries += report_tree(dquot, blk, depth + 1,
613 bitmap, process_dquot,
614 data);
615 }
616 }
617 }
618 freedqbuf(buf);
619 return entries;
620 }
621
find_set_bits(char * bmp,int blocks)622 static uint find_set_bits(char *bmp, int blocks)
623 {
624 uint i, used = 0;
625
626 for (i = 0; i < blocks; i++)
627 if (get_bit(bmp, i))
628 used++;
629 return used;
630 }
631
qtree_scan_dquots(struct quota_handle * h,int (* process_dquot)(struct dquot *,void *),void * data)632 int qtree_scan_dquots(struct quota_handle *h,
633 int (*process_dquot) (struct dquot *, void *),
634 void *data)
635 {
636 char *bitmap;
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();
640
641 if (!dquot)
642 return -1;
643
644 dquot->dq_h = h;
645 if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) {
646 ext2fs_free_mem(&dquot);
647 return -1;
648 }
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);
654 return 0;
655 }
656