1 /* 2 * Implementation of new quotafile format 3 * 4 * Jan Kara <jack (at) 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 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? */ 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 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 50 static int get_index(qid_t id, int depth) 51 { 52 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff; 53 } 54 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 */ 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 */ 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) */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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