1 /* 2 * Implementation of new quotafile format 3 * 4 * Jan Kara <jack (at) suse.cz> - sponsored by SuSE CR 5 */ 6 7 #include "config.h" 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) ext2fs_free_mem(&buf) 22 23 static inline dqbuf_t getdqbuf(void) 24 { 25 dqbuf_t buf; 26 if (ext2fs_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? */ 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 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 51 static int get_index(qid_t id, int depth) 52 { 53 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff; 54 } 55 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 */ 62 static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf) 63 { 64 int err; 65 66 err = h->e2fs_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 */ 75 static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf) 76 { 77 int err; 78 79 err = h->e2fs_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) */ 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 = ext2fs_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 */ 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 = ext2fs_cpu_to_le32(info->dqi_free_blk); 127 dh->dqdh_prev_free = ext2fs_cpu_to_le32(0); 128 dh->dqdh_entries = ext2fs_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 */ 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 = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk = 141 142 ext2fs_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 = ext2fs_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 */ 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 = ext2fs_cpu_to_le32(info->dqi_free_entry); 180 dh->dqdh_prev_free = ext2fs_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 ext2fs_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 */ 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 (ext2fs_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 ext2fs_cpu_to_le16(ext2fs_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 */ 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 = ext2fs_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 ext2fs_cpu_to_le32(newblk); 294 write_blk(h, *treeblk, buf); 295 } else if (newact && ret < 0) { 296 put_free_dqblk(h, buf, *treeblk); 297 } 298 299 out_buf: 300 freedqbuf(buf); 301 return ret; 302 } 303 304 /* Wrapper for inserting quota structure into tree */ 305 static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot) 306 { 307 unsigned int tmp = QT_TREEOFF; 308 309 if (do_insert_tree(h, dquot, &tmp, 0) < 0) 310 log_err("Cannot write quota (id %u): %s", 311 (unsigned int) dquot->dq_id, strerror(errno)); 312 } 313 314 /* Write dquot to file */ 315 void qtree_write_dquot(struct dquot *dquot) 316 { 317 errcode_t retval; 318 unsigned int ret; 319 char *ddquot; 320 struct quota_handle *h = dquot->dq_h; 321 struct qtree_mem_dqinfo *info = 322 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree; 323 log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u", 324 dquot->dq_dqb.u.v2_mdqb.dqb_off, 325 info->dqi_entry_size); 326 retval = ext2fs_get_mem(info->dqi_entry_size, &ddquot); 327 if (retval) { 328 errno = ENOMEM; 329 log_err("Quota write failed (id %u): %s", 330 (unsigned int)dquot->dq_id, strerror(errno)); 331 return; 332 } 333 memset(ddquot, 0, info->dqi_entry_size); 334 335 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) 336 dq_insert_tree(dquot->dq_h, dquot); 337 info->dqi_ops->mem2disk_dqblk(ddquot, dquot); 338 log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u", 339 dquot->dq_dqb.u.v2_mdqb.dqb_off, 340 info->dqi_entry_size); 341 ret = h->e2fs_write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot, 342 info->dqi_entry_size); 343 344 if (ret != info->dqi_entry_size) { 345 if (ret > 0) 346 errno = ENOSPC; 347 log_err("Quota write failed (id %u): %s", 348 (unsigned int)dquot->dq_id, strerror(errno)); 349 } 350 ext2fs_free_mem(&ddquot); 351 } 352 353 /* Free dquot entry in data block */ 354 static void free_dqentry(struct quota_handle *h, struct dquot *dquot, 355 unsigned int blk) 356 { 357 struct qt_disk_dqdbheader *dh; 358 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; 359 dqbuf_t buf = getdqbuf(); 360 361 if (!buf) 362 return; 363 364 if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk) 365 log_err("Quota structure has offset to other block (%u) " 366 "than it should (%u).", blk, 367 (unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >> 368 QT_BLKSIZE_BITS)); 369 370 read_blk(h, blk, buf); 371 dh = (struct qt_disk_dqdbheader *)buf; 372 dh->dqdh_entries = 373 ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1); 374 375 if (!ext2fs_le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ 376 remove_free_dqentry(h, buf, blk); 377 put_free_dqblk(h, buf, blk); 378 } else { 379 memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off & 380 ((1 << QT_BLKSIZE_BITS) - 1)), 381 0, info->dqi_entry_size); 382 383 /* First free entry? */ 384 if (ext2fs_le16_to_cpu(dh->dqdh_entries) == 385 qtree_dqstr_in_blk(info) - 1) 386 /* This will also write data block */ 387 insert_free_dqentry(h, buf, blk); 388 else 389 write_blk(h, blk, buf); 390 } 391 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0; 392 freedqbuf(buf); 393 } 394 395 /* Remove reference to dquot from tree */ 396 static void remove_tree(struct quota_handle *h, struct dquot *dquot, 397 unsigned int * blk, int depth) 398 { 399 dqbuf_t buf = getdqbuf(); 400 unsigned int newblk; 401 __le32 *ref = (__le32 *) buf; 402 403 if (!buf) 404 return; 405 406 read_blk(h, *blk, buf); 407 newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]); 408 if (depth == QT_TREEDEPTH - 1) { 409 free_dqentry(h, dquot, newblk); 410 newblk = 0; 411 } else { 412 remove_tree(h, dquot, &newblk, depth + 1); 413 } 414 415 if (!newblk) { 416 int i; 417 418 ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0); 419 420 /* Block got empty? */ 421 for (i = 0; i < QT_BLKSIZE && !buf[i]; i++); 422 423 /* Don't put the root block into the free block list */ 424 if (i == QT_BLKSIZE && *blk != QT_TREEOFF) { 425 put_free_dqblk(h, buf, *blk); 426 *blk = 0; 427 } else { 428 write_blk(h, *blk, buf); 429 } 430 } 431 freedqbuf(buf); 432 } 433 434 /* Delete dquot from tree */ 435 void qtree_delete_dquot(struct dquot *dquot) 436 { 437 unsigned int tmp = QT_TREEOFF; 438 439 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */ 440 return; 441 remove_tree(dquot->dq_h, dquot, &tmp, 0); 442 } 443 444 /* Find entry in block */ 445 static ext2_loff_t find_block_dqentry(struct quota_handle *h, 446 struct dquot *dquot, unsigned int blk) 447 { 448 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; 449 dqbuf_t buf = getdqbuf(); 450 int i; 451 char *ddquot = buf + sizeof(struct qt_disk_dqdbheader); 452 453 if (!buf) 454 return -ENOMEM; 455 456 read_blk(h, blk, buf); 457 for (i = 0; 458 i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot); 459 i++) 460 ddquot += info->dqi_entry_size; 461 462 if (i == qtree_dqstr_in_blk(info)) 463 log_err("Quota for id %u referenced but not present.", 464 dquot->dq_id); 465 freedqbuf(buf); 466 return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) + 467 i * info->dqi_entry_size; 468 } 469 470 /* Find entry for given id in the tree */ 471 static ext2_loff_t find_tree_dqentry(struct quota_handle *h, 472 struct dquot *dquot, 473 unsigned int blk, int depth) 474 { 475 dqbuf_t buf = getdqbuf(); 476 ext2_loff_t ret = 0; 477 __le32 *ref = (__le32 *) buf; 478 479 if (!buf) 480 return -ENOMEM; 481 482 read_blk(h, blk, buf); 483 ret = 0; 484 blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]); 485 if (!blk) /* No reference? */ 486 goto out_buf; 487 if (depth < QT_TREEDEPTH - 1) 488 ret = find_tree_dqentry(h, dquot, blk, depth + 1); 489 else 490 ret = find_block_dqentry(h, dquot, blk); 491 out_buf: 492 freedqbuf(buf); 493 return ret; 494 } 495 496 /* Find entry for given id in the tree - wrapper function */ 497 static inline ext2_loff_t find_dqentry(struct quota_handle *h, 498 struct dquot *dquot) 499 { 500 return find_tree_dqentry(h, dquot, QT_TREEOFF, 0); 501 } 502 503 /* 504 * Read dquot from disk. 505 */ 506 struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id) 507 { 508 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; 509 ext2_loff_t offset; 510 unsigned int ret; 511 char *ddquot; 512 struct dquot *dquot = get_empty_dquot(); 513 514 if (!dquot) 515 return NULL; 516 if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) { 517 ext2fs_free_mem(&dquot); 518 return NULL; 519 } 520 521 dquot->dq_id = id; 522 dquot->dq_h = h; 523 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0; 524 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk)); 525 526 offset = find_dqentry(h, dquot); 527 if (offset > 0) { 528 dquot->dq_dqb.u.v2_mdqb.dqb_off = offset; 529 ret = h->e2fs_read(&h->qh_qf, offset, ddquot, 530 info->dqi_entry_size); 531 if (ret != info->dqi_entry_size) { 532 if (ret > 0) 533 errno = EIO; 534 log_err("Cannot read quota structure for id %u: %s", 535 dquot->dq_id, strerror(errno)); 536 } 537 info->dqi_ops->disk2mem_dqblk(dquot, ddquot); 538 } 539 ext2fs_free_mem(&ddquot); 540 return dquot; 541 } 542 543 /* 544 * Scan all dquots in file and call callback on each 545 */ 546 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7))) 547 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7))) 548 549 static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap, 550 int (*process_dquot) (struct dquot *, void *), 551 void *data) 552 { 553 struct qtree_mem_dqinfo *info = 554 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree; 555 dqbuf_t buf = getdqbuf(); 556 struct qt_disk_dqdbheader *dh; 557 char *ddata; 558 int entries, i; 559 560 if (!buf) 561 return 0; 562 563 set_bit(bitmap, blk); 564 read_blk(dquot->dq_h, blk, buf); 565 dh = (struct qt_disk_dqdbheader *)buf; 566 ddata = buf + sizeof(struct qt_disk_dqdbheader); 567 entries = ext2fs_le16_to_cpu(dh->dqdh_entries); 568 for (i = 0; i < qtree_dqstr_in_blk(info); 569 i++, ddata += info->dqi_entry_size) 570 if (!qtree_entry_unused(info, ddata)) { 571 dquot->dq_dqb.u.v2_mdqb.dqb_off = 572 (blk << QT_BLKSIZE_BITS) + 573 sizeof(struct qt_disk_dqdbheader) + 574 i * info->dqi_entry_size; 575 info->dqi_ops->disk2mem_dqblk(dquot, ddata); 576 if (process_dquot(dquot, data) < 0) 577 break; 578 } 579 freedqbuf(buf); 580 return entries; 581 } 582 583 static void check_reference(struct quota_handle *h, unsigned int blk) 584 { 585 if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) 586 log_err("Illegal reference (%u >= %u) in %s quota file. " 587 "Quota file is probably corrupted.\n" 588 "Please run e2fsck (8) to fix it.", 589 blk, 590 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks, 591 quota_type2name(h->qh_type)); 592 } 593 594 static int report_tree(struct dquot *dquot, unsigned int blk, int depth, 595 char *bitmap, 596 int (*process_dquot) (struct dquot *, void *), 597 void *data) 598 { 599 int entries = 0, i; 600 dqbuf_t buf = getdqbuf(); 601 __le32 *ref = (__le32 *) buf; 602 603 if (!buf) 604 return 0; 605 606 read_blk(dquot->dq_h, blk, buf); 607 if (depth == QT_TREEDEPTH - 1) { 608 for (i = 0; i < QT_BLKSIZE >> 2; i++) { 609 blk = ext2fs_le32_to_cpu(ref[i]); 610 check_reference(dquot->dq_h, blk); 611 if (blk && !get_bit(bitmap, blk)) 612 entries += report_block(dquot, blk, bitmap, 613 process_dquot, data); 614 } 615 } else { 616 for (i = 0; i < QT_BLKSIZE >> 2; i++) { 617 blk = ext2fs_le32_to_cpu(ref[i]); 618 if (blk) { 619 check_reference(dquot->dq_h, blk); 620 entries += report_tree(dquot, blk, depth + 1, 621 bitmap, process_dquot, 622 data); 623 } 624 } 625 } 626 freedqbuf(buf); 627 return entries; 628 } 629 630 static unsigned int find_set_bits(char *bmp, int blocks) 631 { 632 unsigned int used = 0; 633 int i; 634 635 for (i = 0; i < blocks; i++) 636 if (get_bit(bmp, i)) 637 used++; 638 return used; 639 } 640 641 int qtree_scan_dquots(struct quota_handle *h, 642 int (*process_dquot) (struct dquot *, void *), 643 void *data) 644 { 645 char *bitmap; 646 struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi; 647 struct qtree_mem_dqinfo *info = &v2info->dqi_qtree; 648 struct dquot *dquot = get_empty_dquot(); 649 650 if (!dquot) 651 return -1; 652 653 dquot->dq_h = h; 654 if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) { 655 ext2fs_free_mem(&dquot); 656 return -1; 657 } 658 v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap, 659 process_dquot, data); 660 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks); 661 ext2fs_free_mem(&bitmap); 662 ext2fs_free_mem(&dquot); 663 return 0; 664 } 665