1 /* 2 * Copyright (c) 2007 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/sysctl.h> 36 #include <sys/ioctl_compat.h> 37 38 #include "hammer_util.h" 39 40 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw); 41 static int trim_volume(struct volume_info *vol); 42 static void format_volume(struct volume_info *vol, int nvols,const char *label); 43 static hammer_off_t format_root_directory(const char *label); 44 static uint64_t nowtime(void); 45 static void usage(int exit_code); 46 47 static int ForceOpt; 48 static int64_t BootAreaSize; 49 static int64_t MemAreaSize; 50 static int64_t UndoBufferSize; 51 static int HammerVersion = -1; 52 53 #define GIG (1024LL*1024*1024) 54 55 int 56 main(int ac, char **av) 57 { 58 uint32_t status; 59 off_t total; 60 off_t avg_vol_size; 61 int ch; 62 int i; 63 int nvols; 64 int eflag = 0; 65 const char *label = NULL; 66 struct volume_info *vol; 67 char *fsidstr; 68 69 /* 70 * Sanity check basic filesystem structures. No cookies for us 71 * if it gets broken! 72 */ 73 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE); 74 assert(sizeof(struct hammer_blockmap_layer1) == 32); 75 assert(sizeof(struct hammer_blockmap_layer2) == 16); 76 77 /* 78 * Generate a filesystem id and lookup the filesystem type 79 */ 80 uuidgen(&Hammer_FSId, 1); 81 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status); 82 if (status != uuid_s_ok) { 83 errx(1, "uuids file does not have the DragonFly " 84 "HAMMER filesystem type"); 85 } 86 87 /* 88 * Parse arguments 89 */ 90 while ((ch = getopt(ac, av, "dfEL:b:m:u:hC:V:")) != -1) { 91 switch(ch) { 92 case 'd': 93 ++DebugOpt; 94 break; 95 case 'f': 96 ForceOpt = 1; 97 break; 98 case 'E': 99 eflag = 1; 100 break; 101 case 'L': 102 label = optarg; 103 break; 104 case 'b': 105 BootAreaSize = getsize(optarg, 106 HAMMER_BUFSIZE, 107 HAMMER_BOOT_MAXBYTES, 2); 108 break; 109 case 'm': 110 MemAreaSize = getsize(optarg, 111 HAMMER_BUFSIZE, 112 HAMMER_MEM_MAXBYTES, 2); 113 break; 114 case 'u': 115 UndoBufferSize = getsize(optarg, 116 HAMMER_BIGBLOCK_SIZE, 117 HAMMER_BIGBLOCK_SIZE * 118 HAMMER_UNDO_LAYER2, 2); 119 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0) 120 errx(1, "The minimum UNDO/REDO FIFO size is " 121 "500MB\n"); 122 if (UndoBufferSize < 500*1024*1024) { 123 fprintf(stderr, 124 "WARNING: you have specified an " 125 "UNDO/REDO FIFO size less than 500MB,\n" 126 "which may lead to VFS panics.\n"); 127 } 128 break; 129 case 'h': 130 usage(0); 131 break; 132 case 'C': 133 if (hammer_parse_cache_size(optarg) == -1) 134 usage(1); 135 break; 136 case 'V': 137 HammerVersion = strtol(optarg, NULL, 0); 138 if (HammerVersion < HAMMER_VOL_VERSION_MIN || 139 HammerVersion >= HAMMER_VOL_VERSION_WIP) { 140 errx(1, 141 "I don't understand how to format " 142 "HAMMER version %d\n", 143 HammerVersion); 144 } 145 break; 146 default: 147 usage(1); 148 break; 149 } 150 } 151 152 if (label == NULL) { 153 fprintf(stderr, 154 "newfs_hammer: A filesystem label must be specified\n"); 155 usage(1); 156 } 157 158 if (HammerVersion < 0) { 159 size_t olen = sizeof(HammerVersion); 160 HammerVersion = HAMMER_VOL_VERSION_DEFAULT; 161 if (sysctlbyname("vfs.hammer.supported_version", 162 &HammerVersion, &olen, NULL, 0) == 0) { 163 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) { 164 HammerVersion = HAMMER_VOL_VERSION_WIP - 1; 165 fprintf(stderr, 166 "newfs_hammer: WARNING: HAMMER VFS " 167 "supports higher version than I " 168 "understand,\n" 169 "using version %d\n", 170 HammerVersion); 171 } 172 } else { 173 fprintf(stderr, 174 "newfs_hammer: WARNING: HAMMER VFS not " 175 "loaded, cannot get version info.\n" 176 "Using version %d\n", 177 HAMMER_VOL_VERSION_DEFAULT); 178 } 179 } 180 181 /* 182 * Collect volume information 183 */ 184 ac -= optind; 185 av += optind; 186 nvols = ac; 187 188 if (nvols == 0) { 189 fprintf(stderr, 190 "newfs_hammer: You must specify at least one " 191 "special file (volume)\n"); 192 exit(1); 193 } 194 195 if (nvols > HAMMER_MAX_VOLUMES) { 196 fprintf(stderr, 197 "newfs_hammer: The maximum number of volumes is %d\n", 198 HAMMER_MAX_VOLUMES); 199 exit(1); 200 } 201 202 total = 0; 203 for (i = 0; i < nvols; ++i) { 204 vol = init_volume(i, av[i], O_RDWR); 205 printf("Volume %d %s %-15s size %s\n", 206 vol->vol_no, vol->type, vol->name, 207 sizetostr(vol->size)); 208 209 if (eflag) { 210 int res = trim_volume(vol); 211 if (res == -1 || (res == 1 && ForceOpt == 0)) 212 exit(1); 213 } 214 total += vol->size; 215 } 216 217 /* 218 * Calculate defaults for the boot and memory area sizes. 219 */ 220 avg_vol_size = total / nvols; 221 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size); 222 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size); 223 224 /* 225 * Format the volumes. Format the root volume first so we can 226 * bootstrap the freemap. 227 */ 228 format_volume(get_root_volume(), nvols, label); 229 for (i = 0; i < nvols; ++i) { 230 if (i != HAMMER_ROOT_VOLNO) 231 format_volume(get_volume(i), nvols, label); 232 } 233 234 /* 235 * Print information stored in the root volume header. 236 */ 237 vol = get_root_volume(); 238 uuid_to_string(&Hammer_FSId, &fsidstr, &status); 239 240 printf("---------------------------------------------\n"); 241 printf("%d volume%s total size %s version %d\n", 242 nvols, (nvols == 1 ? "" : "s"), 243 sizetostr(total), HammerVersion); 244 printf("root-volume: %s\n", vol->name); 245 printf("boot-area-size: %s\n", sizetostr(BootAreaSize)); 246 printf("memory-log-size: %s\n", sizetostr(MemAreaSize)); 247 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize)); 248 printf("total-pre-allocated: %s\n", 249 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK)); 250 printf("fsid: %s\n", fsidstr); 251 printf("\n"); 252 printf("NOTE: Please remember that you may have to manually set up a\n" 253 "cron(8) job to prune and reblock the filesystem regularly.\n" 254 "By default, the system automatically runs 'hammer cleanup'\n" 255 "on a nightly basis. The periodic.conf(5) variable\n" 256 "'daily_clean_hammer_enable' can be unset to disable this.\n" 257 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 258 if (total < 10*GIG) { 259 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you " 260 "really should not\n" 261 "try to format a HAMMER filesystem this small.\n"); 262 } 263 if (total < 50*GIG) { 264 printf("\nWARNING: HAMMER filesystems less than 50GB are " 265 "not recommended!\n" 266 "You may have to run 'hammer prune-everything' and " 267 "'hammer reblock'\n" 268 "quite often, even if using a nohistory mount.\n"); 269 } 270 flush_all_volumes(); 271 return(0); 272 } 273 274 static 275 void 276 usage(int exit_code) 277 { 278 fprintf(stderr, 279 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n" 280 " [-C cachesize[:readahead]] [-V version] special ...\n" 281 ); 282 exit(exit_code); 283 } 284 285 /* 286 * Convert a string to a 64 bit signed integer with various requirements. 287 */ 288 static int64_t 289 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 290 { 291 int64_t val; 292 char *ptr; 293 294 val = strtoll(str, &ptr, 0); 295 switch(*ptr) { 296 case 't': 297 case 'T': 298 val *= 1024; 299 /* fall through */ 300 case 'g': 301 case 'G': 302 val *= 1024; 303 /* fall through */ 304 case 'm': 305 case 'M': 306 val *= 1024; 307 /* fall through */ 308 case 'k': 309 case 'K': 310 val *= 1024; 311 break; 312 default: 313 errx(1, "Unknown suffix in number '%s'\n", str); 314 /* not reached */ 315 } 316 if (ptr[1]) { 317 errx(1, "Unknown suffix in number '%s'\n", str); 318 /* not reached */ 319 } 320 if (val < minval) { 321 errx(1, "Value too small: %s, min is %s\n", 322 str, sizetostr(minval)); 323 /* not reached */ 324 } 325 if (val > maxval) { 326 errx(1, "Value too large: %s, max is %s\n", 327 str, sizetostr(maxval)); 328 /* not reached */ 329 } 330 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 331 errx(1, "Value not power of 2: %s\n", str); 332 /* not reached */ 333 } 334 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 335 errx(1, "Value not an integral multiple of %dK: %s", 336 HAMMER_BUFSIZE / 1024, str); 337 /* not reached */ 338 } 339 return(val); 340 } 341 342 /* 343 * Generate a transaction id. Transaction ids are no longer time-based. 344 * Put the nail in the coffin by not making the first one time-based. 345 * 346 * We could start at 1 here but start at 2^32 to reserve a small domain for 347 * possible future use. 348 */ 349 static hammer_tid_t 350 createtid(void) 351 { 352 static hammer_tid_t lasttid; 353 354 if (lasttid == 0) 355 lasttid = 0x0000000100000000ULL; 356 return(lasttid++); 357 } 358 359 static uint64_t 360 nowtime(void) 361 { 362 struct timeval tv; 363 uint64_t xtime; 364 365 gettimeofday(&tv, NULL); 366 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 367 return(xtime); 368 } 369 370 /* 371 * TRIM the volume, but only if the backing store is a DEVICE 372 */ 373 static 374 int 375 trim_volume(struct volume_info *vol) 376 { 377 size_t olen; 378 char *dev_name, *p; 379 char sysctl_name[64]; 380 int trim_enabled; 381 off_t ioarg[2]; 382 383 if (strcmp(vol->type, "DEVICE")) { 384 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name); 385 return(1); 386 } 387 if (strncmp(vol->name, "/dev/da", 7)) { 388 fprintf(stderr, "%s does not support the TRIM command\n", 389 vol->name); 390 return(1); 391 } 392 393 /* Extract a number from /dev/da?s? */ 394 dev_name = strdup(vol->name); 395 p = strtok(dev_name + strlen("/dev/da"), "s"); 396 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p); 397 free(dev_name); 398 399 trim_enabled = 0; 400 olen = sizeof(trim_enabled); 401 402 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) { 403 fprintf(stderr, "%s (%s) does not support the TRIM command\n", 404 vol->name, sysctl_name); 405 return(1); 406 } 407 if (!trim_enabled) { 408 fprintf(stderr, "Erase device option selected, but sysctl (%s) " 409 "is not enabled\n", sysctl_name); 410 return(1); 411 } 412 413 ioarg[0] = vol->device_offset; 414 ioarg[1] = vol->size; 415 416 printf("Trimming %s %s, sectors %llu-%llu\n", 417 vol->type, vol->name, 418 (unsigned long long)ioarg[0] / 512, 419 (unsigned long long)ioarg[1] / 512); 420 421 if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) { 422 fprintf(stderr, "Device trim failed\n"); 423 return(-1); 424 } 425 426 return(0); 427 } 428 429 /* 430 * Format a HAMMER volume. 431 */ 432 static 433 void 434 format_volume(struct volume_info *vol, int nvols, const char *label) 435 { 436 struct volume_info *root_vol; 437 hammer_volume_ondisk_t ondisk; 438 int64_t freeblks; 439 int64_t freebytes; 440 int64_t vol_buf_size; 441 hammer_off_t vol_alloc; 442 int i; 443 444 /* 445 * Initialize basic information in the on-disk volume structure. 446 */ 447 ondisk = vol->ondisk; 448 449 ondisk->vol_fsid = Hammer_FSId; 450 ondisk->vol_fstype = Hammer_FSType; 451 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label); 452 ondisk->vol_no = vol->vol_no; 453 ondisk->vol_count = nvols; 454 ondisk->vol_version = HammerVersion; 455 456 /* 457 * Reserve space for (future) header junk, setup our poor-man's 458 * big-block allocator. 459 */ 460 vol_alloc = HAMMER_VOL_ALLOC; 461 462 ondisk->vol_bot_beg = vol_alloc; 463 vol_alloc += BootAreaSize; 464 ondisk->vol_mem_beg = vol_alloc; 465 vol_alloc += MemAreaSize; 466 467 /* 468 * The remaining area is the zone 2 buffer allocation area. 469 */ 470 ondisk->vol_buf_beg = vol_alloc; 471 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 472 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk); 473 474 if (vol_buf_size < 0) { 475 errx(1, "volume %d %s is too small to hold the volume header", 476 vol->vol_no, vol->name); 477 } 478 479 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) { 480 errx(1, "volume %d %s is too large", vol->vol_no, vol->name); 481 } 482 483 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO; 484 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 485 486 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 487 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 488 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 489 490 /* 491 * Format the root volume. 492 */ 493 if (vol->vol_no == HAMMER_ROOT_VOLNO) { 494 /* 495 * Check freemap counts before formatting 496 */ 497 freeblks = count_freemap(vol); 498 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64; 499 if (freebytes < 10*GIG && ForceOpt == 0) { 500 errx(1, "Cannot create a HAMMER filesystem less than 10GB " 501 "unless you use -f\n(for the size of Volume %d). " 502 "HAMMER filesystems less than 50GB are not " 503 "recommended.\n", HAMMER_ROOT_VOLNO); 504 } 505 506 /* 507 * Starting TID 508 */ 509 ondisk->vol0_next_tid = createtid(); 510 511 /* 512 * Format freemap. vol0_stat_freebigblocks is 513 * the number of big-blocks available for anything 514 * other than freemap zone at this point. 515 */ 516 format_freemap(vol); 517 assert(ondisk->vol0_stat_freebigblocks == 0); 518 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol); 519 520 /* 521 * Format zones that are mapped to zone-2. 522 */ 523 for (i = 0; i < HAMMER_MAX_ZONES; ++i) { 524 if (hammer_is_zone2_mapped_index(i)) 525 format_blockmap(vol, i, 0); 526 } 527 528 /* 529 * Format undo zone. Formatting decrements 530 * vol0_stat_freebigblocks whenever a new big-block 531 * is allocated for undo zone. 532 */ 533 format_undomap(vol, &UndoBufferSize); 534 assert(ondisk->vol0_stat_bigblocks == 0); 535 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks; 536 537 /* 538 * Format the root directory. Formatting decrements 539 * vol0_stat_freebigblocks whenever a new big-block 540 * is allocated for required zones. 541 */ 542 ondisk->vol0_btree_root = format_root_directory(label); 543 ++ondisk->vol0_stat_inodes; /* root inode */ 544 } else { 545 freeblks = initialize_freemap(vol); 546 root_vol = get_root_volume(); 547 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 548 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 549 } 550 } 551 552 /* 553 * Format the root directory. 554 */ 555 static 556 hammer_off_t 557 format_root_directory(const char *label) 558 { 559 hammer_off_t btree_off; 560 hammer_off_t idata_off; 561 hammer_off_t pfsd_off; 562 hammer_tid_t create_tid; 563 hammer_node_ondisk_t bnode; 564 hammer_inode_data_t idata; 565 hammer_pseudofs_data_t pfsd; 566 struct buffer_info *data_buffer0 = NULL; 567 struct buffer_info *data_buffer1 = NULL; 568 struct buffer_info *data_buffer2 = NULL; 569 hammer_btree_elm_t elm; 570 uint64_t xtime; 571 572 /* 573 * Allocate zero-filled root btree node, inode and pfs 574 */ 575 bnode = alloc_btree_element(&btree_off, &data_buffer0); 576 idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1); 577 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 578 create_tid = createtid(); 579 xtime = nowtime(); 580 581 /* 582 * Populate the inode data and inode record for the root directory. 583 */ 584 idata->version = HAMMER_INODE_DATA_VERSION; 585 idata->mode = 0755; 586 idata->ctime = xtime; 587 idata->mtime = xtime; 588 idata->atime = xtime; 589 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 590 idata->size = 0; 591 idata->nlinks = 1; 592 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 593 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 594 if (HammerVersion >= HAMMER_VOL_VERSION_SIX) 595 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1; 596 597 /* 598 * Populate the PFS data for the root PFS. 599 */ 600 pfsd->sync_low_tid = 1; 601 pfsd->sync_beg_tid = 0; 602 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 603 pfsd->shared_uuid = Hammer_FSId; 604 pfsd->unique_uuid = Hammer_FSId; 605 pfsd->mirror_flags = 0; 606 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 607 608 /* 609 * Create the root of the B-Tree. The root is a leaf node so we 610 * do not have to worry about boundary elements. 611 */ 612 bnode->parent = 0; /* no parent */ 613 bnode->count = 2; 614 bnode->type = HAMMER_BTREE_TYPE_LEAF; 615 bnode->mirror_tid = 0; 616 617 /* 618 * Create the first node element for the inode. 619 */ 620 elm = &bnode->elms[0]; 621 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 622 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 623 HAMMER_LOCALIZE_INODE; 624 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 625 elm->leaf.base.key = 0; 626 elm->leaf.base.create_tid = create_tid; 627 elm->leaf.base.delete_tid = 0; 628 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 629 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 630 elm->leaf.create_ts = (uint32_t)time(NULL); 631 632 elm->leaf.data_offset = idata_off; 633 elm->leaf.data_len = sizeof(*idata); 634 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 635 636 /* 637 * Create the second node element for the PFS data. 638 * This is supposed to be a record part of the root ip (inode), 639 * so it should have the same obj_type value as above. 640 */ 641 elm = &bnode->elms[1]; 642 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 643 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 644 HAMMER_LOCALIZE_MISC; 645 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 646 elm->leaf.base.key = 0; 647 elm->leaf.base.create_tid = create_tid; 648 elm->leaf.base.delete_tid = 0; 649 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 650 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 651 elm->leaf.create_ts = (uint32_t)time(NULL); 652 653 elm->leaf.data_offset = pfsd_off; 654 elm->leaf.data_len = sizeof(*pfsd); 655 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 656 657 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 658 659 rel_buffer(data_buffer0); 660 rel_buffer(data_buffer1); 661 rel_buffer(data_buffer2); 662 663 return(btree_off); 664 } 665