1 /*
2 * Implementation of new quotafile format
3 *
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
5 * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
6 */
7
8 #include "config.h"
9 #include <sys/types.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include "common.h"
17 #include "quotaio_tree.h"
18 #include "quotaio.h"
19
20 typedef char *dqbuf_t;
21
22 #define freedqbuf(buf) quota_free_mem(&buf)
23
getdqbuf(void)24 static inline dqbuf_t getdqbuf(void)
25 {
26 dqbuf_t buf;
27 if (quota_get_memzero(QT_BLKSIZE, &buf)) {
28 log_err("Failed to allocate dqbuf");
29 return NULL;
30 }
31
32 return buf;
33 }
34
35 /* Is given dquot empty? */
qtree_entry_unused(struct qtree_mem_dqinfo * info,char * disk)36 int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
37 {
38 unsigned int i;
39
40 for (i = 0; i < info->dqi_entry_size; i++)
41 if (disk[i])
42 return 0;
43 return 1;
44 }
45
qtree_dqstr_in_blk(struct qtree_mem_dqinfo * info)46 int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
47 {
48 return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
49 info->dqi_entry_size;
50 }
51
get_index(qid_t id,int depth)52 static int get_index(qid_t id, int depth)
53 {
54 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
55 }
56
mark_quotafile_info_dirty(struct quota_handle * h)57 static inline void mark_quotafile_info_dirty(struct quota_handle *h)
58 {
59 h->qh_io_flags |= IOFL_INFODIRTY;
60 }
61
62 /* Read given block */
read_blk(struct quota_handle * h,unsigned int blk,dqbuf_t buf)63 static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
64 {
65 int err;
66
67 err = h->read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
68 QT_BLKSIZE);
69 if (err < 0)
70 log_err("Cannot read block %u: %s", blk, strerror(errno));
71 else if (err != QT_BLKSIZE)
72 memset(buf + err, 0, QT_BLKSIZE - err);
73 }
74
75 /* Write block */
write_blk(struct quota_handle * h,unsigned int blk,dqbuf_t buf)76 static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
77 {
78 int err;
79
80 err = h->write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
81 QT_BLKSIZE);
82 if (err < 0 && errno != ENOSPC)
83 log_err("Cannot write block (%u): %s", blk, strerror(errno));
84 if (err != QT_BLKSIZE)
85 return -ENOSPC;
86 return 0;
87 }
88
89 /* Get free block in file (either from free list or create new one) */
get_free_dqblk(struct quota_handle * h)90 static int get_free_dqblk(struct quota_handle *h)
91 {
92 dqbuf_t buf = getdqbuf();
93 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
94 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
95 int blk;
96
97 if (!buf)
98 return -ENOMEM;
99
100 if (info->dqi_free_blk) {
101 blk = info->dqi_free_blk;
102 read_blk(h, blk, buf);
103 info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
104 } else {
105 memset(buf, 0, QT_BLKSIZE);
106 /* Assure block allocation... */
107 if (write_blk(h, info->dqi_blocks, buf) < 0) {
108 freedqbuf(buf);
109 log_err("Cannot allocate new quota block "
110 "(out of disk space).");
111 return -ENOSPC;
112 }
113 blk = info->dqi_blocks++;
114 }
115 mark_quotafile_info_dirty(h);
116 freedqbuf(buf);
117 return blk;
118 }
119
120 /* Put given block to free list */
put_free_dqblk(struct quota_handle * h,dqbuf_t buf,unsigned int blk)121 static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf,
122 unsigned int blk)
123 {
124 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
125 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
126
127 dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
128 dh->dqdh_prev_free = cpu_to_le32(0);
129 dh->dqdh_entries = cpu_to_le16(0);
130 info->dqi_free_blk = blk;
131 mark_quotafile_info_dirty(h);
132 write_blk(h, blk, buf);
133 }
134
135 /* Remove given block from the list of blocks with free entries */
remove_free_dqentry(struct quota_handle * h,dqbuf_t buf,unsigned int blk)136 static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf,
137 unsigned int blk)
138 {
139 dqbuf_t tmpbuf = getdqbuf();
140 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
141 unsigned int nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk =
142 le32_to_cpu(dh->dqdh_prev_free);
143
144 if (!tmpbuf)
145 return;
146
147 if (nextblk) {
148 read_blk(h, nextblk, tmpbuf);
149 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
150 dh->dqdh_prev_free;
151 write_blk(h, nextblk, tmpbuf);
152 }
153 if (prevblk) {
154 read_blk(h, prevblk, tmpbuf);
155 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
156 dh->dqdh_next_free;
157 write_blk(h, prevblk, tmpbuf);
158 } else {
159 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
160 mark_quotafile_info_dirty(h);
161 }
162 freedqbuf(tmpbuf);
163 dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
164 write_blk(h, blk, buf); /* No matter whether write succeeds
165 * block is out of list */
166 }
167
168 /* Insert given block to the beginning of list with free entries */
insert_free_dqentry(struct quota_handle * h,dqbuf_t buf,unsigned int blk)169 static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf,
170 unsigned int blk)
171 {
172 dqbuf_t tmpbuf = getdqbuf();
173 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
174 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
175
176 if (!tmpbuf)
177 return;
178
179 dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry);
180 dh->dqdh_prev_free = cpu_to_le32(0);
181 write_blk(h, blk, buf);
182 if (info->dqi_free_entry) {
183 read_blk(h, info->dqi_free_entry, tmpbuf);
184 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
185 cpu_to_le32(blk);
186 write_blk(h, info->dqi_free_entry, tmpbuf);
187 }
188 freedqbuf(tmpbuf);
189 info->dqi_free_entry = blk;
190 mark_quotafile_info_dirty(h);
191 }
192
193 /* Find space for dquot */
find_free_dqentry(struct quota_handle * h,struct dquot * dquot,int * err)194 static unsigned int find_free_dqentry(struct quota_handle *h,
195 struct dquot *dquot, int *err)
196 {
197 int blk, i;
198 struct qt_disk_dqdbheader *dh;
199 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
200 char *ddquot;
201 dqbuf_t buf;
202
203 *err = 0;
204 buf = getdqbuf();
205 if (!buf) {
206 *err = -ENOMEM;
207 return 0;
208 }
209
210 dh = (struct qt_disk_dqdbheader *)buf;
211 if (info->dqi_free_entry) {
212 blk = info->dqi_free_entry;
213 read_blk(h, blk, buf);
214 } else {
215 blk = get_free_dqblk(h);
216 if (blk < 0) {
217 freedqbuf(buf);
218 *err = blk;
219 return 0;
220 }
221 memset(buf, 0, QT_BLKSIZE);
222 info->dqi_free_entry = blk;
223 mark_quotafile_info_dirty(h);
224 }
225
226 /* Block will be full? */
227 if (le16_to_cpu(dh->dqdh_entries) + 1 >=
228 qtree_dqstr_in_blk(info))
229 remove_free_dqentry(h, buf, blk);
230
231 dh->dqdh_entries =
232 cpu_to_le16(le16_to_cpu(dh->dqdh_entries) + 1);
233 /* Find free structure in block */
234 ddquot = buf + sizeof(struct qt_disk_dqdbheader);
235 for (i = 0;
236 i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
237 i++)
238 ddquot += info->dqi_entry_size;
239
240 if (i == qtree_dqstr_in_blk(info))
241 log_err("find_free_dqentry(): Data block full unexpectedly.");
242
243 write_blk(h, blk, buf);
244 dquot->dq_dqb.u.v2_mdqb.dqb_off =
245 (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
246 i * info->dqi_entry_size;
247 freedqbuf(buf);
248 return blk;
249 }
250
251 /* Insert reference to structure into the trie */
do_insert_tree(struct quota_handle * h,struct dquot * dquot,unsigned int * treeblk,int depth)252 static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
253 unsigned int * treeblk, int depth)
254 {
255 dqbuf_t buf;
256 int newson = 0, newact = 0;
257 __le32 *ref;
258 unsigned int newblk;
259 int ret = 0;
260
261 log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
262 buf = getdqbuf();
263 if (!buf)
264 return -ENOMEM;
265
266 if (!*treeblk) {
267 ret = get_free_dqblk(h);
268 if (ret < 0)
269 goto out_buf;
270 *treeblk = ret;
271 memset(buf, 0, QT_BLKSIZE);
272 newact = 1;
273 } else {
274 read_blk(h, *treeblk, buf);
275 }
276
277 ref = (__le32 *) buf;
278 newblk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
279 if (!newblk)
280 newson = 1;
281 if (depth == QT_TREEDEPTH - 1) {
282 if (newblk)
283 log_err("Inserting already present quota entry "
284 "(block %u).",
285 ref[get_index(dquot->dq_id, depth)]);
286 newblk = find_free_dqentry(h, dquot, &ret);
287 } else {
288 ret = do_insert_tree(h, dquot, &newblk, depth + 1);
289 }
290
291 if (newson && ret >= 0) {
292 ref[get_index(dquot->dq_id, depth)] =
293 cpu_to_le32(newblk);
294 ret = write_blk(h, *treeblk, buf);
295 if (ret)
296 goto out_buf;
297 } else if (newact && ret < 0) {
298 put_free_dqblk(h, buf, *treeblk);
299 }
300
301 out_buf:
302 freedqbuf(buf);
303 return ret;
304 }
305
306 /* Wrapper for inserting quota structure into tree */
dq_insert_tree(struct quota_handle * h,struct dquot * dquot)307 static int dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
308 {
309 unsigned int tmp = QT_TREEOFF;
310 int err;
311
312 err = do_insert_tree(h, dquot, &tmp, 0);
313 if (err < 0)
314 log_err("Cannot write quota (id %u): %s",
315 (unsigned int) dquot->dq_id, strerror(errno));
316 return err;
317 }
318
319 /* Write dquot to file */
qtree_write_dquot(struct dquot * dquot)320 int qtree_write_dquot(struct dquot *dquot)
321 {
322 errcode_t retval;
323 unsigned int ret;
324 char *ddquot;
325 struct quota_handle *h = dquot->dq_h;
326 struct qtree_mem_dqinfo *info =
327 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
328
329 log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
330 dquot->dq_dqb.u.v2_mdqb.dqb_off,
331 info->dqi_entry_size);
332 retval = quota_get_mem(info->dqi_entry_size, &ddquot);
333 if (retval) {
334 log_err("Quota write failed (id %u): %s",
335 (unsigned int)dquot->dq_id, strerror(errno));
336 return -ENOMEM;
337 }
338 memset(ddquot, 0, info->dqi_entry_size);
339
340 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) {
341 if (dq_insert_tree(dquot->dq_h, dquot)) {
342 quota_free_mem(&ddquot);
343 return -EIO;
344 }
345 }
346 info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
347 log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
348 dquot->dq_dqb.u.v2_mdqb.dqb_off,
349 info->dqi_entry_size);
350 ret = h->write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
351 info->dqi_entry_size);
352
353 if (ret != info->dqi_entry_size) {
354 log_err("Quota write failed (id %u): %s",
355 (unsigned int)dquot->dq_id, strerror(errno));
356 quota_free_mem(&ddquot);
357 return ret;
358 }
359 quota_free_mem(&ddquot);
360 return 0;
361 }
362
363 /* Free dquot entry in data block */
free_dqentry(struct quota_handle * h,struct dquot * dquot,unsigned int blk)364 static void free_dqentry(struct quota_handle *h, struct dquot *dquot,
365 unsigned int blk)
366 {
367 struct qt_disk_dqdbheader *dh;
368 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
369 dqbuf_t buf = getdqbuf();
370
371 if (!buf)
372 return;
373
374 if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
375 log_err("Quota structure has offset to other block (%u) "
376 "than it should (%u).", blk,
377 (unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
378 QT_BLKSIZE_BITS));
379
380 read_blk(h, blk, buf);
381 dh = (struct qt_disk_dqdbheader *)buf;
382 dh->dqdh_entries =
383 cpu_to_le16(le16_to_cpu(dh->dqdh_entries) - 1);
384
385 if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
386 remove_free_dqentry(h, buf, blk);
387 put_free_dqblk(h, buf, blk);
388 } else {
389 memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
390 ((1 << QT_BLKSIZE_BITS) - 1)),
391 0, info->dqi_entry_size);
392
393 /* First free entry? */
394 if (le16_to_cpu(dh->dqdh_entries) ==
395 qtree_dqstr_in_blk(info) - 1)
396 /* This will also write data block */
397 insert_free_dqentry(h, buf, blk);
398 else
399 write_blk(h, blk, buf);
400 }
401 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
402 freedqbuf(buf);
403 }
404
405 /* Remove reference to dquot from tree */
remove_tree(struct quota_handle * h,struct dquot * dquot,unsigned int * blk,int depth)406 static void remove_tree(struct quota_handle *h, struct dquot *dquot,
407 unsigned int * blk, int depth)
408 {
409 dqbuf_t buf = getdqbuf();
410 unsigned int newblk;
411 __le32 *ref = (__le32 *) buf;
412
413 if (!buf)
414 return;
415
416 read_blk(h, *blk, buf);
417 newblk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
418 if (depth == QT_TREEDEPTH - 1) {
419 free_dqentry(h, dquot, newblk);
420 newblk = 0;
421 } else {
422 remove_tree(h, dquot, &newblk, depth + 1);
423 }
424
425 if (!newblk) {
426 int i;
427
428 ref[get_index(dquot->dq_id, depth)] = cpu_to_le32(0);
429
430 /* Block got empty? */
431 for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
432
433 /* Don't put the root block into the free block list */
434 if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
435 put_free_dqblk(h, buf, *blk);
436 *blk = 0;
437 } else {
438 write_blk(h, *blk, buf);
439 }
440 }
441 freedqbuf(buf);
442 }
443
444 /* Delete dquot from tree */
qtree_delete_dquot(struct dquot * dquot)445 void qtree_delete_dquot(struct dquot *dquot)
446 {
447 unsigned int tmp = QT_TREEOFF;
448
449 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
450 return;
451 remove_tree(dquot->dq_h, dquot, &tmp, 0);
452 }
453
454 /* Find entry in block */
find_block_dqentry(struct quota_handle * h,struct dquot * dquot,unsigned int blk)455 static long find_block_dqentry(struct quota_handle *h,
456 struct dquot *dquot, unsigned int blk)
457 {
458 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
459 dqbuf_t buf = getdqbuf();
460 int i;
461 char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
462
463 if (!buf)
464 return -ENOMEM;
465
466 read_blk(h, blk, buf);
467 for (i = 0;
468 i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
469 i++)
470 ddquot += info->dqi_entry_size;
471
472 if (i == qtree_dqstr_in_blk(info))
473 log_err("Quota for id %u referenced but not present.",
474 dquot->dq_id);
475 freedqbuf(buf);
476 return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
477 i * info->dqi_entry_size;
478 }
479
480 /* Find entry for given id in the tree */
find_tree_dqentry(struct quota_handle * h,struct dquot * dquot,unsigned int blk,int depth)481 static long find_tree_dqentry(struct quota_handle *h,
482 struct dquot *dquot,
483 unsigned int blk, int depth)
484 {
485 dqbuf_t buf = getdqbuf();
486 long ret = 0;
487 __le32 *ref = (__le32 *) buf;
488
489 if (!buf)
490 return -ENOMEM;
491
492 read_blk(h, blk, buf);
493 ret = 0;
494 blk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
495 if (!blk) /* No reference? */
496 goto out_buf;
497 if (depth < QT_TREEDEPTH - 1)
498 ret = find_tree_dqentry(h, dquot, blk, depth + 1);
499 else
500 ret = find_block_dqentry(h, dquot, blk);
501 out_buf:
502 freedqbuf(buf);
503 return ret;
504 }
505
506 /* Find entry for given id in the tree - wrapper function */
find_dqentry(struct quota_handle * h,struct dquot * dquot)507 static inline long find_dqentry(struct quota_handle *h,
508 struct dquot *dquot)
509 {
510 return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
511 }
512
513 /*
514 * Read dquot from disk.
515 */
qtree_read_dquot(struct quota_handle * h,qid_t id)516 struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
517 {
518 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
519 long offset;
520 unsigned int ret;
521 char *ddquot;
522 struct dquot *dquot = get_empty_dquot();
523
524 if (!dquot)
525 return NULL;
526 if (quota_get_mem(info->dqi_entry_size, &ddquot)) {
527 quota_free_mem(&dquot);
528 return NULL;
529 }
530
531 dquot->dq_id = id;
532 dquot->dq_h = h;
533 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
534 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
535
536 offset = find_dqentry(h, dquot);
537 if (offset > 0) {
538 dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
539 ret = h->read(&h->qh_qf, offset, ddquot,
540 info->dqi_entry_size);
541 if (ret != info->dqi_entry_size) {
542 if (ret > 0)
543 errno = EIO;
544 log_err("Cannot read quota structure for id %u: %s",
545 dquot->dq_id, strerror(errno));
546 }
547 info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
548 }
549 quota_free_mem(&ddquot);
550 return dquot;
551 }
552
553 /*
554 * Scan all dquots in file and call callback on each
555 */
556 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
557 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
558
report_block(struct dquot * dquot,unsigned int blk,char * bitmap,int (* process_dquot)(struct dquot *,void *),void * data)559 static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
560 int (*process_dquot) (struct dquot *, void *),
561 void *data)
562 {
563 struct qtree_mem_dqinfo *info =
564 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
565 dqbuf_t buf = getdqbuf();
566 struct qt_disk_dqdbheader *dh;
567 char *ddata;
568 int entries, i;
569
570 if (!buf)
571 return -1;
572
573 set_bit(bitmap, blk);
574 read_blk(dquot->dq_h, blk, buf);
575 dh = (struct qt_disk_dqdbheader *)buf;
576 ddata = buf + sizeof(struct qt_disk_dqdbheader);
577 entries = le16_to_cpu(dh->dqdh_entries);
578 for (i = 0; i < qtree_dqstr_in_blk(info);
579 i++, ddata += info->dqi_entry_size)
580 if (!qtree_entry_unused(info, ddata)) {
581 dquot->dq_dqb.u.v2_mdqb.dqb_off =
582 (blk << QT_BLKSIZE_BITS) +
583 sizeof(struct qt_disk_dqdbheader) +
584 i * info->dqi_entry_size;
585 info->dqi_ops->disk2mem_dqblk(dquot, ddata);
586 if (process_dquot(dquot, data) < 0)
587 break;
588 }
589 freedqbuf(buf);
590 return entries;
591 }
592
check_reference(struct quota_handle * h,unsigned int blk)593 static int check_reference(struct quota_handle *h, unsigned int blk)
594 {
595 if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
596 log_err("Illegal reference (%u >= %u) in %s quota file",
597 blk,
598 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
599 quota_type2name(h->qh_type));
600 return -1;
601 }
602 return 0;
603 }
604
605 /* Return 0 for successful run */
report_tree(struct dquot * dquot,unsigned int blk,int depth,char * bitmap,int * entries,int (* process_dquot)(struct dquot *,void *),void * data)606 static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
607 char *bitmap, int *entries,
608 int (*process_dquot) (struct dquot *, void *),
609 void *data)
610 {
611 int i;
612 dqbuf_t buf = getdqbuf();
613 __le32 *ref = (__le32 *) buf;
614
615 if (!buf)
616 return -1;
617
618 read_blk(dquot->dq_h, blk, buf);
619 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
620 blk = le32_to_cpu(ref[i]);
621 if (blk == 0)
622 continue;
623
624 if (check_reference(dquot->dq_h, blk))
625 break;
626
627 if (depth == QT_TREEDEPTH - 1) {
628 if (!get_bit(bitmap, blk)) {
629 int num_entry = report_block(dquot, blk, bitmap,
630 process_dquot, data);
631 if (num_entry < 0)
632 break;
633 *entries += num_entry;
634 }
635 } else {
636 if (report_tree(dquot, blk, depth + 1, bitmap, entries,
637 process_dquot, data))
638 break;
639 }
640 }
641 freedqbuf(buf);
642 return (i < QT_BLKSIZE >> 2) ? -1 : 0;
643 }
644
find_set_bits(char * bmp,int blocks)645 static unsigned int find_set_bits(char *bmp, int blocks)
646 {
647 unsigned int used = 0;
648 int i;
649
650 for (i = 0; i < blocks; i++)
651 if (get_bit(bmp, i))
652 used++;
653 return used;
654 }
655
qtree_scan_dquots(struct quota_handle * h,int (* process_dquot)(struct dquot *,void *),void * data)656 int qtree_scan_dquots(struct quota_handle *h,
657 int (*process_dquot) (struct dquot *, void *),
658 void *data)
659 {
660 struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
661 struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
662 struct dquot *dquot = get_empty_dquot();
663 char *bitmap = NULL;
664 int ret = -1;
665 int entries = 0;
666
667 if (!dquot)
668 return -1;
669
670 dquot->dq_h = h;
671 if (quota_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap))
672 goto out;
673 if (report_tree(dquot, QT_TREEOFF, 0, bitmap, &entries, process_dquot,
674 data))
675 goto out;
676
677 v2info->dqi_used_entries = entries;
678 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
679 ret = 0;
680
681 out:
682 if (bitmap)
683 quota_free_mem(&bitmap);
684 if (dquot)
685 quota_free_mem(&dquot);
686
687 return ret;
688 }
689