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 206 /* 207 * Load up information on the volume and initialize 208 * its remaining fields. 209 */ 210 check_volume(vol); 211 printf("Volume %d %s %-15s size %s\n", 212 vol->vol_no, vol->type, vol->name, 213 sizetostr(vol->size)); 214 215 if (eflag) { 216 int res = trim_volume(vol); 217 if (res == -1 || (res == 1 && ForceOpt == 0)) 218 exit(1); 219 } 220 total += vol->size; 221 } 222 223 /* 224 * Calculate defaults for the boot and memory area sizes. 225 */ 226 avg_vol_size = total / nvols; 227 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size); 228 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size); 229 230 /* 231 * Format the volumes. Format the root volume first so we can 232 * bootstrap the freemap. 233 */ 234 format_volume(get_root_volume(), nvols, label); 235 for (i = 0; i < nvols; ++i) { 236 if (i != HAMMER_ROOT_VOLNO) 237 format_volume(get_volume(i), nvols, label); 238 } 239 240 /* 241 * Print information stored in the root volume header. 242 */ 243 vol = get_root_volume(); 244 uuid_to_string(&Hammer_FSId, &fsidstr, &status); 245 246 printf("---------------------------------------------\n"); 247 printf("%d volume%s total size %s version %d\n", 248 nvols, (nvols == 1 ? "" : "s"), 249 sizetostr(total), HammerVersion); 250 printf("root-volume: %s\n", vol->name); 251 printf("boot-area-size: %s\n", sizetostr(BootAreaSize)); 252 printf("memory-log-size: %s\n", sizetostr(MemAreaSize)); 253 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize)); 254 printf("total-pre-allocated: %s\n", 255 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK)); 256 printf("fsid: %s\n", fsidstr); 257 printf("\n"); 258 printf("NOTE: Please remember that you may have to manually set up a\n" 259 "cron(8) job to prune and reblock the filesystem regularly.\n" 260 "By default, the system automatically runs 'hammer cleanup'\n" 261 "on a nightly basis. The periodic.conf(5) variable\n" 262 "'daily_clean_hammer_enable' can be unset to disable this.\n" 263 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 264 if (total < 10*GIG) { 265 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you " 266 "really should not\n" 267 "try to format a HAMMER filesystem this small.\n"); 268 } 269 if (total < 50*GIG) { 270 printf("\nWARNING: HAMMER filesystems less than 50GB are " 271 "not recommended!\n" 272 "You may have to run 'hammer prune-everything' and " 273 "'hammer reblock'\n" 274 "quite often, even if using a nohistory mount.\n"); 275 } 276 flush_all_volumes(); 277 return(0); 278 } 279 280 static 281 void 282 usage(int exit_code) 283 { 284 fprintf(stderr, 285 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n" 286 " [-C cachesize[:readahead]] [-V version] special ...\n" 287 ); 288 exit(exit_code); 289 } 290 291 /* 292 * Convert a string to a 64 bit signed integer with various requirements. 293 */ 294 static int64_t 295 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 296 { 297 int64_t val; 298 char *ptr; 299 300 val = strtoll(str, &ptr, 0); 301 switch(*ptr) { 302 case 't': 303 case 'T': 304 val *= 1024; 305 /* fall through */ 306 case 'g': 307 case 'G': 308 val *= 1024; 309 /* fall through */ 310 case 'm': 311 case 'M': 312 val *= 1024; 313 /* fall through */ 314 case 'k': 315 case 'K': 316 val *= 1024; 317 break; 318 default: 319 errx(1, "Unknown suffix in number '%s'\n", str); 320 /* not reached */ 321 } 322 if (ptr[1]) { 323 errx(1, "Unknown suffix in number '%s'\n", str); 324 /* not reached */ 325 } 326 if (val < minval) { 327 errx(1, "Value too small: %s, min is %s\n", 328 str, sizetostr(minval)); 329 /* not reached */ 330 } 331 if (val > maxval) { 332 errx(1, "Value too large: %s, max is %s\n", 333 str, sizetostr(maxval)); 334 /* not reached */ 335 } 336 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 337 errx(1, "Value not power of 2: %s\n", str); 338 /* not reached */ 339 } 340 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 341 errx(1, "Value not an integral multiple of %dK: %s", 342 HAMMER_BUFSIZE / 1024, str); 343 /* not reached */ 344 } 345 return(val); 346 } 347 348 /* 349 * Generate a transaction id. Transaction ids are no longer time-based. 350 * Put the nail in the coffin by not making the first one time-based. 351 * 352 * We could start at 1 here but start at 2^32 to reserve a small domain for 353 * possible future use. 354 */ 355 static hammer_tid_t 356 createtid(void) 357 { 358 static hammer_tid_t lasttid; 359 360 if (lasttid == 0) 361 lasttid = 0x0000000100000000ULL; 362 return(lasttid++); 363 } 364 365 static uint64_t 366 nowtime(void) 367 { 368 struct timeval tv; 369 uint64_t xtime; 370 371 gettimeofday(&tv, NULL); 372 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 373 return(xtime); 374 } 375 376 /* 377 * TRIM the volume, but only if the backing store is a DEVICE 378 */ 379 static 380 int 381 trim_volume(struct volume_info *vol) 382 { 383 size_t olen; 384 char *dev_name, *p; 385 char sysctl_name[64]; 386 int trim_enabled; 387 off_t ioarg[2]; 388 389 if (strcmp(vol->type, "DEVICE")) { 390 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name); 391 return(1); 392 } 393 if (strncmp(vol->name, "/dev/da", 7)) { 394 fprintf(stderr, "%s does not support the TRIM command\n", 395 vol->name); 396 return(1); 397 } 398 399 /* Extract a number from /dev/da?s? */ 400 dev_name = strdup(vol->name); 401 p = strtok(dev_name + strlen("/dev/da"), "s"); 402 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p); 403 free(dev_name); 404 405 trim_enabled = 0; 406 olen = sizeof(trim_enabled); 407 408 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) { 409 fprintf(stderr, "%s (%s) does not support the TRIM command\n", 410 vol->name, sysctl_name); 411 return(1); 412 } 413 if (!trim_enabled) { 414 fprintf(stderr, "Erase device option selected, but sysctl (%s) " 415 "is not enabled\n", sysctl_name); 416 return(1); 417 } 418 419 ioarg[0] = vol->device_offset; 420 ioarg[1] = vol->size; 421 422 printf("Trimming %s %s, sectors %llu-%llu\n", 423 vol->type, vol->name, 424 (unsigned long long)ioarg[0] / 512, 425 (unsigned long long)ioarg[1] / 512); 426 427 if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) { 428 fprintf(stderr, "Device trim failed\n"); 429 return(-1); 430 } 431 432 return(0); 433 } 434 435 /* 436 * Format a HAMMER volume. 437 */ 438 static 439 void 440 format_volume(struct volume_info *vol, int nvols, const char *label) 441 { 442 struct volume_info *root_vol; 443 hammer_volume_ondisk_t ondisk; 444 int64_t freeblks; 445 int64_t freebytes; 446 int64_t vol_buf_size; 447 hammer_off_t vol_alloc; 448 int i; 449 450 /* 451 * Initialize basic information in the on-disk volume structure. 452 */ 453 ondisk = vol->ondisk; 454 455 ondisk->vol_fsid = Hammer_FSId; 456 ondisk->vol_fstype = Hammer_FSType; 457 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label); 458 ondisk->vol_no = vol->vol_no; 459 ondisk->vol_count = nvols; 460 ondisk->vol_version = HammerVersion; 461 462 /* 463 * Reserve space for (future) header junk, setup our poor-man's 464 * big-block allocator. 465 */ 466 vol_alloc = HAMMER_VOL_ALLOC; 467 468 ondisk->vol_bot_beg = vol_alloc; 469 vol_alloc += BootAreaSize; 470 ondisk->vol_mem_beg = vol_alloc; 471 vol_alloc += MemAreaSize; 472 473 /* 474 * The remaining area is the zone 2 buffer allocation area. 475 */ 476 ondisk->vol_buf_beg = vol_alloc; 477 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 478 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk); 479 480 if (vol_buf_size < 0) { 481 errx(1, "volume %d %s is too small to hold the volume header", 482 vol->vol_no, vol->name); 483 } 484 485 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) { 486 errx(1, "volume %d %s is too large", vol->vol_no, vol->name); 487 } 488 489 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO; 490 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 491 492 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 493 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 494 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 495 496 /* 497 * Format the root volume. 498 */ 499 if (vol->vol_no == HAMMER_ROOT_VOLNO) { 500 /* 501 * Check freemap counts before formatting 502 */ 503 freeblks = count_freemap(vol); 504 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64; 505 if (freebytes < 10*GIG && ForceOpt == 0) { 506 errx(1, "Cannot create a HAMMER filesystem less than 10GB " 507 "unless you use -f\n(for the size of Volume %d). " 508 "HAMMER filesystems less than 50GB are not " 509 "recommended.\n", HAMMER_ROOT_VOLNO); 510 } 511 512 /* 513 * Starting TID 514 */ 515 ondisk->vol0_next_tid = createtid(); 516 517 /* 518 * Format freemap. vol0_stat_freebigblocks is 519 * the number of big-blocks available for anything 520 * other than freemap zone at this point. 521 */ 522 format_freemap(vol); 523 assert(ondisk->vol0_stat_freebigblocks == 0); 524 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol); 525 526 /* 527 * Format zones that are mapped to zone-2. 528 */ 529 for (i = 0; i < HAMMER_MAX_ZONES; ++i) { 530 if (hammer_is_zone2_mapped_index(i)) 531 format_blockmap(vol, i, 0); 532 } 533 534 /* 535 * Format undo zone. Formatting decrements 536 * vol0_stat_freebigblocks whenever a new big-block 537 * is allocated for undo zone. 538 */ 539 format_undomap(vol, &UndoBufferSize); 540 assert(ondisk->vol0_stat_bigblocks == 0); 541 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks; 542 543 /* 544 * Format the root directory. Formatting decrements 545 * vol0_stat_freebigblocks whenever a new big-block 546 * is allocated for required zones. 547 */ 548 ondisk->vol0_btree_root = format_root_directory(label); 549 ++ondisk->vol0_stat_inodes; /* root inode */ 550 } else { 551 freeblks = initialize_freemap(vol); 552 root_vol = get_root_volume(); 553 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 554 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 555 } 556 } 557 558 /* 559 * Format the root directory. 560 */ 561 static 562 hammer_off_t 563 format_root_directory(const char *label) 564 { 565 hammer_off_t btree_off; 566 hammer_off_t pfsd_off; 567 hammer_off_t data_off; 568 hammer_tid_t create_tid; 569 hammer_node_ondisk_t bnode; 570 hammer_inode_data_t idata; 571 hammer_pseudofs_data_t pfsd; 572 struct buffer_info *data_buffer0 = NULL; 573 struct buffer_info *data_buffer1 = NULL; 574 struct buffer_info *data_buffer2 = NULL; 575 hammer_btree_elm_t elm; 576 uint64_t xtime; 577 578 /* 579 * Allocate zero-filled root btree node, inode and pfs 580 */ 581 bnode = alloc_btree_element(&btree_off, &data_buffer0); 582 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1); 583 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 584 create_tid = createtid(); 585 xtime = nowtime(); 586 587 /* 588 * Populate the inode data and inode record for the root directory. 589 */ 590 idata->version = HAMMER_INODE_DATA_VERSION; 591 idata->mode = 0755; 592 idata->ctime = xtime; 593 idata->mtime = xtime; 594 idata->atime = xtime; 595 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 596 idata->size = 0; 597 idata->nlinks = 1; 598 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 599 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 600 if (HammerVersion >= HAMMER_VOL_VERSION_SIX) 601 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1; 602 603 /* 604 * Populate the PFS data for the root PFS. 605 */ 606 pfsd->sync_low_tid = 1; 607 pfsd->sync_beg_tid = 0; 608 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 609 pfsd->shared_uuid = Hammer_FSId; 610 pfsd->unique_uuid = Hammer_FSId; 611 pfsd->reserved01 = 0; 612 pfsd->mirror_flags = 0; 613 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 614 615 /* 616 * Create the root of the B-Tree. The root is a leaf node so we 617 * do not have to worry about boundary elements. 618 */ 619 bnode->count = 2; 620 bnode->type = HAMMER_BTREE_TYPE_LEAF; 621 622 /* 623 * Create the first node element for the inode. 624 */ 625 elm = &bnode->elms[0]; 626 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 627 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 628 HAMMER_LOCALIZE_INODE; 629 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 630 elm->leaf.base.key = 0; 631 elm->leaf.base.create_tid = create_tid; 632 elm->leaf.base.delete_tid = 0; 633 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 634 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 635 elm->leaf.create_ts = (uint32_t)time(NULL); 636 637 elm->leaf.data_offset = data_off; 638 elm->leaf.data_len = sizeof(*idata); 639 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 640 641 /* 642 * Create the second node element for the PFS data. 643 * This is supposed to be a record part of the root ip (inode), 644 * so it should have the same obj_type value as above. 645 */ 646 elm = &bnode->elms[1]; 647 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 648 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 649 HAMMER_LOCALIZE_MISC; 650 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 651 elm->leaf.base.key = 0; 652 elm->leaf.base.create_tid = create_tid; 653 elm->leaf.base.delete_tid = 0; 654 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 655 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 656 elm->leaf.create_ts = (uint32_t)time(NULL); 657 658 elm->leaf.data_offset = pfsd_off; 659 elm->leaf.data_len = sizeof(*pfsd); 660 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 661 662 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 663 664 rel_buffer(data_buffer0); 665 rel_buffer(data_buffer1); 666 rel_buffer(data_buffer2); 667 668 return(btree_off); 669 } 670