icount.c revision 9288e3be665bb8d5657d7f710687a50fad859acf
1/* 2 * icount.c --- an efficient inode count abstraction 3 * 4 * Copyright (C) 1997 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include "config.h" 13#if HAVE_UNISTD_H 14#include <unistd.h> 15#endif 16#include <string.h> 17#include <stdio.h> 18#include <sys/stat.h> 19#include <fcntl.h> 20#include <errno.h> 21 22#include "ext2_fs.h" 23#include "ext2fs.h" 24#include "tdb.h" 25 26/* 27 * The data storage strategy used by icount relies on the observation 28 * that most inode counts are either zero (for non-allocated inodes), 29 * one (for most files), and only a few that are two or more 30 * (directories and files that are linked to more than one directory). 31 * 32 * Also, e2fsck tends to load the icount data sequentially. 33 * 34 * So, we use an inode bitmap to indicate which inodes have a count of 35 * one, and then use a sorted list to store the counts for inodes 36 * which are greater than one. 37 * 38 * We also use an optional bitmap to indicate which inodes are already 39 * in the sorted list, to speed up the use of this abstraction by 40 * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, 41 * so this extra bitmap avoids searching the sorted list to see if a 42 * particular inode is on the sorted list already. 43 */ 44 45struct ext2_icount_el { 46 ext2_ino_t ino; 47 __u32 count; 48}; 49 50struct ext2_icount { 51 errcode_t magic; 52 ext2fs_inode_bitmap single; 53 ext2fs_inode_bitmap multiple; 54 ext2_ino_t count; 55 ext2_ino_t size; 56 ext2_ino_t num_inodes; 57 ext2_ino_t cursor; 58 struct ext2_icount_el *list; 59 struct ext2_icount_el *last_lookup; 60 char *tdb_fn; 61 TDB_CONTEXT *tdb; 62}; 63 64/* 65 * We now use a 32-bit counter field because it doesn't cost us 66 * anything extra for the in-memory data structure, due to alignment 67 * padding. But there's no point changing the interface if most of 68 * the time we only care if the number is bigger than 65,000 or not. 69 * So use the following translation function to return a 16-bit count. 70 */ 71#define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x)) 72 73void ext2fs_free_icount(ext2_icount_t icount) 74{ 75 if (!icount) 76 return; 77 78 icount->magic = 0; 79 if (icount->list) 80 ext2fs_free_mem(&icount->list); 81 if (icount->single) 82 ext2fs_free_inode_bitmap(icount->single); 83 if (icount->multiple) 84 ext2fs_free_inode_bitmap(icount->multiple); 85 if (icount->tdb) 86 tdb_close(icount->tdb); 87 if (icount->tdb_fn) { 88 unlink(icount->tdb_fn); 89 free(icount->tdb_fn); 90 } 91 92 ext2fs_free_mem(&icount); 93} 94 95static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret) 96{ 97 ext2_icount_t icount; 98 errcode_t retval; 99 100 *ret = 0; 101 102 retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); 103 if (retval) 104 return retval; 105 memset(icount, 0, sizeof(struct ext2_icount)); 106 107 retval = ext2fs_allocate_inode_bitmap(fs, "icount", &icount->single); 108 if (retval) 109 goto errout; 110 111 if (flags & EXT2_ICOUNT_OPT_INCREMENT) { 112 retval = ext2fs_allocate_inode_bitmap(fs, "icount_inc", 113 &icount->multiple); 114 if (retval) 115 goto errout; 116 } else 117 icount->multiple = 0; 118 119 icount->magic = EXT2_ET_MAGIC_ICOUNT; 120 icount->num_inodes = fs->super->s_inodes_count; 121 122 *ret = icount; 123 return 0; 124 125errout: 126 ext2fs_free_icount(icount); 127 return(retval); 128} 129 130struct uuid { 131 __u32 time_low; 132 __u16 time_mid; 133 __u16 time_hi_and_version; 134 __u16 clock_seq; 135 __u8 node[6]; 136}; 137 138static void unpack_uuid(void *in, struct uuid *uu) 139{ 140 __u8 *ptr = in; 141 __u32 tmp; 142 143 tmp = *ptr++; 144 tmp = (tmp << 8) | *ptr++; 145 tmp = (tmp << 8) | *ptr++; 146 tmp = (tmp << 8) | *ptr++; 147 uu->time_low = tmp; 148 149 tmp = *ptr++; 150 tmp = (tmp << 8) | *ptr++; 151 uu->time_mid = tmp; 152 153 tmp = *ptr++; 154 tmp = (tmp << 8) | *ptr++; 155 uu->time_hi_and_version = tmp; 156 157 tmp = *ptr++; 158 tmp = (tmp << 8) | *ptr++; 159 uu->clock_seq = tmp; 160 161 memcpy(uu->node, ptr, 6); 162} 163 164static void uuid_unparse(void *uu, char *out) 165{ 166 struct uuid uuid; 167 168 unpack_uuid(uu, &uuid); 169 sprintf(out, 170 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 171 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, 172 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, 173 uuid.node[0], uuid.node[1], uuid.node[2], 174 uuid.node[3], uuid.node[4], uuid.node[5]); 175} 176 177errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, 178 int flags, ext2_icount_t *ret) 179{ 180 ext2_icount_t icount; 181 errcode_t retval; 182 char *fn, uuid[40]; 183 ext2_ino_t num_inodes; 184 int fd; 185 186 retval = alloc_icount(fs, flags, &icount); 187 if (retval) 188 return retval; 189 190 retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn); 191 if (retval) 192 goto errout; 193 uuid_unparse(fs->super->s_uuid, uuid); 194 sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid); 195 fd = mkstemp(fn); 196 197 /* 198 * This is an overestimate of the size that we will need; the 199 * ideal value is the number of used inodes with a count 200 * greater than 1. OTOH the times when we really need this is 201 * with the backup programs that use lots of hard links, in 202 * which case the number of inodes in use approaches the ideal 203 * value. 204 */ 205 num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count; 206 207 icount->tdb_fn = fn; 208 icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC, 209 O_RDWR | O_CREAT | O_TRUNC, 0600); 210 if (icount->tdb) { 211 close(fd); 212 *ret = icount; 213 return 0; 214 } 215 216 retval = errno; 217 close(fd); 218 219errout: 220 ext2fs_free_icount(icount); 221 return(retval); 222} 223 224errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, 225 ext2_icount_t hint, ext2_icount_t *ret) 226{ 227 ext2_icount_t icount; 228 errcode_t retval; 229 size_t bytes; 230 ext2_ino_t i; 231 232 if (hint) { 233 EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); 234 if (hint->size > size) 235 size = (size_t) hint->size; 236 } 237 238 retval = alloc_icount(fs, flags, &icount); 239 if (retval) 240 return retval; 241 242 if (size) { 243 icount->size = size; 244 } else { 245 /* 246 * Figure out how many special case inode counts we will 247 * have. We know we will need one for each directory; 248 * we also need to reserve some extra room for file links 249 */ 250 retval = ext2fs_get_num_dirs(fs, &icount->size); 251 if (retval) 252 goto errout; 253 icount->size += fs->super->s_inodes_count / 50; 254 } 255 256 bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); 257#if 0 258 printf("Icount allocated %u entries, %d bytes.\n", 259 icount->size, bytes); 260#endif 261 retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el), 262 &icount->list); 263 if (retval) 264 goto errout; 265 memset(icount->list, 0, bytes); 266 267 icount->count = 0; 268 icount->cursor = 0; 269 270 /* 271 * Populate the sorted list with those entries which were 272 * found in the hint icount (since those are ones which will 273 * likely need to be in the sorted list this time around). 274 */ 275 if (hint) { 276 for (i=0; i < hint->count; i++) 277 icount->list[i].ino = hint->list[i].ino; 278 icount->count = hint->count; 279 } 280 281 *ret = icount; 282 return 0; 283 284errout: 285 ext2fs_free_icount(icount); 286 return(retval); 287} 288 289errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, 290 unsigned int size, 291 ext2_icount_t *ret) 292{ 293 return ext2fs_create_icount2(fs, flags, size, 0, ret); 294} 295 296/* 297 * insert_icount_el() --- Insert a new entry into the sorted list at a 298 * specified position. 299 */ 300static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, 301 ext2_ino_t ino, int pos) 302{ 303 struct ext2_icount_el *el; 304 errcode_t retval; 305 ext2_ino_t new_size = 0; 306 int num; 307 308 if (icount->last_lookup && icount->last_lookup->ino == ino) 309 return icount->last_lookup; 310 311 if (icount->count >= icount->size) { 312 if (icount->count) { 313 new_size = icount->list[(unsigned)icount->count-1].ino; 314 new_size = (ext2_ino_t) (icount->count * 315 ((float) icount->num_inodes / new_size)); 316 } 317 if (new_size < (icount->size + 100)) 318 new_size = icount->size + 100; 319#if 0 320 printf("Reallocating icount %u entries...\n", new_size); 321#endif 322 retval = ext2fs_resize_mem((size_t) icount->size * 323 sizeof(struct ext2_icount_el), 324 (size_t) new_size * 325 sizeof(struct ext2_icount_el), 326 &icount->list); 327 if (retval) 328 return 0; 329 icount->size = new_size; 330 } 331 num = (int) icount->count - pos; 332 if (num < 0) 333 return 0; /* should never happen */ 334 if (num) { 335 memmove(&icount->list[pos+1], &icount->list[pos], 336 sizeof(struct ext2_icount_el) * num); 337 } 338 icount->count++; 339 el = &icount->list[pos]; 340 el->count = 0; 341 el->ino = ino; 342 icount->last_lookup = el; 343 return el; 344} 345 346/* 347 * get_icount_el() --- given an inode number, try to find icount 348 * information in the sorted list. If the create flag is set, 349 * and we can't find an entry, create one in the sorted list. 350 */ 351static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, 352 ext2_ino_t ino, int create) 353{ 354 float range; 355 int low, high, mid; 356 ext2_ino_t lowval, highval; 357 358 if (!icount || !icount->list) 359 return 0; 360 361 if (create && ((icount->count == 0) || 362 (ino > icount->list[(unsigned)icount->count-1].ino))) { 363 return insert_icount_el(icount, ino, (unsigned) icount->count); 364 } 365 if (icount->count == 0) 366 return 0; 367 368 if (icount->cursor >= icount->count) 369 icount->cursor = 0; 370 if (ino == icount->list[icount->cursor].ino) 371 return &icount->list[icount->cursor++]; 372#if 0 373 printf("Non-cursor get_icount_el: %u\n", ino); 374#endif 375 low = 0; 376 high = (int) icount->count-1; 377 while (low <= high) { 378 mid = ((unsigned)low + (unsigned)high) >> 1; 379 if (ino == icount->list[mid].ino) { 380 icount->cursor = mid+1; 381 return &icount->list[mid]; 382 } 383 if (ino < icount->list[mid].ino) 384 high = mid-1; 385 else 386 low = mid+1; 387 } 388 /* 389 * If we need to create a new entry, it should be right at 390 * low (where high will be left at low-1). 391 */ 392 if (create) 393 return insert_icount_el(icount, ino, low); 394 return 0; 395} 396 397static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino, 398 __u32 count) 399{ 400 struct ext2_icount_el *el; 401 TDB_DATA key, data; 402 403 if (icount->tdb) { 404 key.dptr = (unsigned char *) &ino; 405 key.dsize = sizeof(ext2_ino_t); 406 data.dptr = (unsigned char *) &count; 407 data.dsize = sizeof(__u32); 408 if (count) { 409 if (tdb_store(icount->tdb, key, data, TDB_REPLACE)) 410 return tdb_error(icount->tdb) + 411 EXT2_ET_TDB_SUCCESS; 412 } else { 413 if (tdb_delete(icount->tdb, key)) 414 return tdb_error(icount->tdb) + 415 EXT2_ET_TDB_SUCCESS; 416 } 417 return 0; 418 } 419 420 el = get_icount_el(icount, ino, 1); 421 if (!el) 422 return EXT2_ET_NO_MEMORY; 423 424 el->count = count; 425 return 0; 426} 427 428static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino, 429 __u32 *count) 430{ 431 struct ext2_icount_el *el; 432 TDB_DATA key, data; 433 434 if (icount->tdb) { 435 key.dptr = (unsigned char *) &ino; 436 key.dsize = sizeof(ext2_ino_t); 437 438 data = tdb_fetch(icount->tdb, key); 439 if (data.dptr == NULL) { 440 *count = 0; 441 return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS; 442 } 443 444 *count = *((__u32 *) data.dptr); 445 free(data.dptr); 446 return 0; 447 } 448 el = get_icount_el(icount, ino, 0); 449 if (!el) { 450 *count = 0; 451 return ENOENT; 452 } 453 454 *count = el->count; 455 return 0; 456} 457 458errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) 459{ 460 errcode_t ret = 0; 461 unsigned int i; 462 const char *bad = "bad icount"; 463 464 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); 465 466 if (icount->count > icount->size) { 467 fprintf(out, "%s: count > size\n", bad); 468 return EXT2_ET_INVALID_ARGUMENT; 469 } 470 for (i=1; i < icount->count; i++) { 471 if (icount->list[i-1].ino >= icount->list[i].ino) { 472 fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", 473 bad, i-1, icount->list[i-1].ino, 474 i, icount->list[i].ino); 475 ret = EXT2_ET_INVALID_ARGUMENT; 476 } 477 } 478 return ret; 479} 480 481errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) 482{ 483 __u32 val; 484 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); 485 486 if (!ino || (ino > icount->num_inodes)) 487 return EXT2_ET_INVALID_ARGUMENT; 488 489 if (ext2fs_test_inode_bitmap2(icount->single, ino)) { 490 *ret = 1; 491 return 0; 492 } 493 if (icount->multiple && 494 !ext2fs_test_inode_bitmap2(icount->multiple, ino)) { 495 *ret = 0; 496 return 0; 497 } 498 get_inode_count(icount, ino, &val); 499 *ret = icount_16_xlate(val); 500 return 0; 501} 502 503errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, 504 __u16 *ret) 505{ 506 __u32 curr_value; 507 508 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); 509 510 if (!ino || (ino > icount->num_inodes)) 511 return EXT2_ET_INVALID_ARGUMENT; 512 513 if (ext2fs_test_inode_bitmap2(icount->single, ino)) { 514 /* 515 * If the existing count is 1, then we know there is 516 * no entry in the list. 517 */ 518 if (set_inode_count(icount, ino, 2)) 519 return EXT2_ET_NO_MEMORY; 520 curr_value = 2; 521 ext2fs_unmark_inode_bitmap2(icount->single, ino); 522 } else if (icount->multiple) { 523 /* 524 * The count is either zero or greater than 1; if the 525 * inode is set in icount->multiple, then there should 526 * be an entry in the list, so we need to fix it. 527 */ 528 if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) { 529 get_inode_count(icount, ino, &curr_value); 530 curr_value++; 531 if (set_inode_count(icount, ino, curr_value)) 532 return EXT2_ET_NO_MEMORY; 533 } else { 534 /* 535 * The count was zero; mark the single bitmap 536 * and return. 537 */ 538 ext2fs_mark_inode_bitmap2(icount->single, ino); 539 if (ret) 540 *ret = 1; 541 return 0; 542 } 543 } else { 544 /* 545 * The count is either zero or greater than 1; try to 546 * find an entry in the list to determine which. 547 */ 548 get_inode_count(icount, ino, &curr_value); 549 curr_value++; 550 if (set_inode_count(icount, ino, curr_value)) 551 return EXT2_ET_NO_MEMORY; 552 } 553 if (icount->multiple) 554 ext2fs_mark_inode_bitmap2(icount->multiple, ino); 555 if (ret) 556 *ret = icount_16_xlate(curr_value); 557 return 0; 558} 559 560errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, 561 __u16 *ret) 562{ 563 __u32 curr_value; 564 565 if (!ino || (ino > icount->num_inodes)) 566 return EXT2_ET_INVALID_ARGUMENT; 567 568 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); 569 570 if (ext2fs_test_inode_bitmap2(icount->single, ino)) { 571 ext2fs_unmark_inode_bitmap2(icount->single, ino); 572 if (icount->multiple) 573 ext2fs_unmark_inode_bitmap2(icount->multiple, ino); 574 else { 575 set_inode_count(icount, ino, 0); 576 } 577 if (ret) 578 *ret = 0; 579 return 0; 580 } 581 582 if (icount->multiple && 583 !ext2fs_test_inode_bitmap2(icount->multiple, ino)) 584 return EXT2_ET_INVALID_ARGUMENT; 585 586 get_inode_count(icount, ino, &curr_value); 587 if (!curr_value) 588 return EXT2_ET_INVALID_ARGUMENT; 589 curr_value--; 590 if (set_inode_count(icount, ino, curr_value)) 591 return EXT2_ET_NO_MEMORY; 592 593 if (curr_value == 1) 594 ext2fs_mark_inode_bitmap2(icount->single, ino); 595 if ((curr_value == 0) && icount->multiple) 596 ext2fs_unmark_inode_bitmap2(icount->multiple, ino); 597 598 if (ret) 599 *ret = icount_16_xlate(curr_value); 600 return 0; 601} 602 603errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, 604 __u16 count) 605{ 606 if (!ino || (ino > icount->num_inodes)) 607 return EXT2_ET_INVALID_ARGUMENT; 608 609 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); 610 611 if (count == 1) { 612 ext2fs_mark_inode_bitmap2(icount->single, ino); 613 if (icount->multiple) 614 ext2fs_unmark_inode_bitmap2(icount->multiple, ino); 615 return 0; 616 } 617 if (count == 0) { 618 ext2fs_unmark_inode_bitmap2(icount->single, ino); 619 if (icount->multiple) { 620 /* 621 * If the icount->multiple bitmap is enabled, 622 * we can just clear both bitmaps and we're done 623 */ 624 ext2fs_unmark_inode_bitmap2(icount->multiple, ino); 625 } else 626 set_inode_count(icount, ino, 0); 627 return 0; 628 } 629 630 if (set_inode_count(icount, ino, count)) 631 return EXT2_ET_NO_MEMORY; 632 ext2fs_unmark_inode_bitmap2(icount->single, ino); 633 if (icount->multiple) 634 ext2fs_mark_inode_bitmap2(icount->multiple, ino); 635 return 0; 636} 637 638ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) 639{ 640 if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) 641 return 0; 642 643 return icount->size; 644} 645 646#ifdef DEBUG 647 648ext2_filsys test_fs; 649ext2_icount_t icount; 650 651#define EXIT 0x00 652#define FETCH 0x01 653#define STORE 0x02 654#define INCREMENT 0x03 655#define DECREMENT 0x04 656 657struct test_program { 658 int cmd; 659 ext2_ino_t ino; 660 __u16 arg; 661 __u16 expected; 662}; 663 664struct test_program prog[] = { 665 { STORE, 42, 42, 42 }, 666 { STORE, 1, 1, 1 }, 667 { STORE, 2, 2, 2 }, 668 { STORE, 3, 3, 3 }, 669 { STORE, 10, 1, 1 }, 670 { STORE, 42, 0, 0 }, 671 { INCREMENT, 5, 0, 1 }, 672 { INCREMENT, 5, 0, 2 }, 673 { INCREMENT, 5, 0, 3 }, 674 { INCREMENT, 5, 0, 4 }, 675 { DECREMENT, 5, 0, 3 }, 676 { DECREMENT, 5, 0, 2 }, 677 { DECREMENT, 5, 0, 1 }, 678 { DECREMENT, 5, 0, 0 }, 679 { FETCH, 10, 0, 1 }, 680 { FETCH, 1, 0, 1 }, 681 { FETCH, 2, 0, 2 }, 682 { FETCH, 3, 0, 3 }, 683 { INCREMENT, 1, 0, 2 }, 684 { DECREMENT, 2, 0, 1 }, 685 { DECREMENT, 2, 0, 0 }, 686 { FETCH, 12, 0, 0 }, 687 { EXIT, 0, 0, 0 } 688}; 689 690struct test_program extended[] = { 691 { STORE, 1, 1, 1 }, 692 { STORE, 2, 2, 2 }, 693 { STORE, 3, 3, 3 }, 694 { STORE, 4, 4, 4 }, 695 { STORE, 5, 5, 5 }, 696 { STORE, 6, 1, 1 }, 697 { STORE, 7, 2, 2 }, 698 { STORE, 8, 3, 3 }, 699 { STORE, 9, 4, 4 }, 700 { STORE, 10, 5, 5 }, 701 { STORE, 11, 1, 1 }, 702 { STORE, 12, 2, 2 }, 703 { STORE, 13, 3, 3 }, 704 { STORE, 14, 4, 4 }, 705 { STORE, 15, 5, 5 }, 706 { STORE, 16, 1, 1 }, 707 { STORE, 17, 2, 2 }, 708 { STORE, 18, 3, 3 }, 709 { STORE, 19, 4, 4 }, 710 { STORE, 20, 5, 5 }, 711 { STORE, 21, 1, 1 }, 712 { STORE, 22, 2, 2 }, 713 { STORE, 23, 3, 3 }, 714 { STORE, 24, 4, 4 }, 715 { STORE, 25, 5, 5 }, 716 { STORE, 26, 1, 1 }, 717 { STORE, 27, 2, 2 }, 718 { STORE, 28, 3, 3 }, 719 { STORE, 29, 4, 4 }, 720 { STORE, 30, 5, 5 }, 721 { EXIT, 0, 0, 0 } 722}; 723 724/* 725 * Setup the variables for doing the inode scan test. 726 */ 727static void setup(void) 728{ 729 errcode_t retval; 730 struct ext2_super_block param; 731 732 initialize_ext2_error_table(); 733 734 memset(¶m, 0, sizeof(param)); 735 ext2fs_blocks_count_set(¶m, 12000); 736 737 retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, 738 test_io_manager, &test_fs); 739 if (retval) { 740 com_err("setup", retval, 741 "while initializing filesystem"); 742 exit(1); 743 } 744 retval = ext2fs_allocate_tables(test_fs); 745 if (retval) { 746 com_err("setup", retval, 747 "while allocating tables for test filesystem"); 748 exit(1); 749 } 750} 751 752int run_test(int flags, int size, char *dir, struct test_program *prog) 753{ 754 errcode_t retval; 755 ext2_icount_t icount; 756 struct test_program *pc; 757 __u16 result; 758 int problem = 0; 759 760 if (dir) { 761 retval = ext2fs_create_icount_tdb(test_fs, dir, 762 flags, &icount); 763 if (retval) { 764 com_err("run_test", retval, 765 "while creating icount using tdb"); 766 exit(1); 767 } 768 } else { 769 retval = ext2fs_create_icount2(test_fs, flags, size, 0, 770 &icount); 771 if (retval) { 772 com_err("run_test", retval, "while creating icount"); 773 exit(1); 774 } 775 } 776 for (pc = prog; pc->cmd != EXIT; pc++) { 777 switch (pc->cmd) { 778 case FETCH: 779 printf("icount_fetch(%u) = ", pc->ino); 780 break; 781 case STORE: 782 retval = ext2fs_icount_store(icount, pc->ino, pc->arg); 783 if (retval) { 784 com_err("run_test", retval, 785 "while calling icount_store"); 786 exit(1); 787 } 788 printf("icount_store(%u, %u) = ", pc->ino, pc->arg); 789 break; 790 case INCREMENT: 791 retval = ext2fs_icount_increment(icount, pc->ino, 0); 792 if (retval) { 793 com_err("run_test", retval, 794 "while calling icount_increment"); 795 exit(1); 796 } 797 printf("icount_increment(%u) = ", pc->ino); 798 break; 799 case DECREMENT: 800 retval = ext2fs_icount_decrement(icount, pc->ino, 0); 801 if (retval) { 802 com_err("run_test", retval, 803 "while calling icount_decrement"); 804 exit(1); 805 } 806 printf("icount_decrement(%u) = ", pc->ino); 807 break; 808 } 809 retval = ext2fs_icount_fetch(icount, pc->ino, &result); 810 if (retval) { 811 com_err("run_test", retval, 812 "while calling icount_fetch"); 813 exit(1); 814 } 815 printf("%u (%s)\n", result, (result == pc->expected) ? 816 "OK" : "NOT OK"); 817 if (result != pc->expected) 818 problem++; 819 } 820 printf("icount size is %u\n", ext2fs_get_icount_size(icount)); 821 retval = ext2fs_icount_validate(icount, stdout); 822 if (retval) { 823 com_err("run_test", retval, "while calling icount_validate"); 824 exit(1); 825 } 826 ext2fs_free_icount(icount); 827 return problem; 828} 829 830 831int main(int argc, char **argv) 832{ 833 int failed = 0; 834 835 setup(); 836 printf("Standard icount run:\n"); 837 failed += run_test(0, 0, 0, prog); 838 printf("\nMultiple bitmap test:\n"); 839 failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog); 840 printf("\nResizing icount:\n"); 841 failed += run_test(0, 3, 0, extended); 842 printf("\nStandard icount run with tdb:\n"); 843 failed += run_test(0, 0, ".", prog); 844 printf("\nMultiple bitmap test with tdb:\n"); 845 failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog); 846 if (failed) 847 printf("FAILED!\n"); 848 return failed; 849} 850#endif 851