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