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 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.44 2008/08/21 23:32:27 thomas Exp $ 35 */ 36 37 #include "newfs_hammer.h" 38 39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw); 40 static const char *sizetostr(off_t size); 41 static void check_volume(struct volume_info *vol); 42 static void format_volume(struct volume_info *vol, int nvols,const char *label, 43 off_t total_size); 44 static hammer_off_t format_root(const char *label); 45 static u_int64_t nowtime(void); 46 static void usage(void); 47 48 static int ForceOpt = 0; 49 static int HammerVersion = -1; 50 51 #define GIG (1024LL*1024*1024) 52 53 int 54 main(int ac, char **av) 55 { 56 u_int32_t status; 57 off_t total; 58 int ch; 59 int i; 60 const char *label = NULL; 61 struct volume_info *vol; 62 char *fsidstr; 63 64 /* 65 * Sanity check basic filesystem structures. No cookies for us 66 * if it gets broken! 67 */ 68 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE); 69 assert(sizeof(struct hammer_blockmap_layer1) == 32); 70 assert(sizeof(struct hammer_blockmap_layer2) == 16); 71 72 /* 73 * Generate a filesystem id and lookup the filesystem type 74 */ 75 uuidgen(&Hammer_FSId, 1); 76 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status); 77 if (status != uuid_s_ok) { 78 errx(1, "uuids file does not have the DragonFly " 79 "HAMMER filesystem type"); 80 } 81 82 /* 83 * Parse arguments 84 */ 85 while ((ch = getopt(ac, av, "fL:b:m:u:V:")) != -1) { 86 switch(ch) { 87 case 'f': 88 ForceOpt = 1; 89 break; 90 case 'L': 91 label = optarg; 92 break; 93 case 'b': 94 BootAreaSize = getsize(optarg, 95 HAMMER_BUFSIZE, 96 HAMMER_BOOT_MAXBYTES, 2); 97 break; 98 case 'm': 99 MemAreaSize = getsize(optarg, 100 HAMMER_BUFSIZE, 101 HAMMER_MEM_MAXBYTES, 2); 102 break; 103 case 'u': 104 UndoBufferSize = getsize(optarg, 105 HAMMER_LARGEBLOCK_SIZE, 106 HAMMER_LARGEBLOCK_SIZE * 107 HAMMER_UNDO_LAYER2, 2); 108 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0) 109 errx(1, "The minimum UNDO/REDO FIFO size is " 110 "500MB\n"); 111 if (UndoBufferSize < 500*1024*1024) { 112 fprintf(stderr, 113 "WARNING: you have specified an " 114 "UNDO/REDO FIFO size less than 500MB,\n" 115 "which may lead to VFS panics.\n"); 116 } 117 break; 118 case 'V': 119 HammerVersion = strtol(optarg, NULL, 0); 120 if (HammerVersion < HAMMER_VOL_VERSION_MIN || 121 HammerVersion >= HAMMER_VOL_VERSION_WIP) { 122 errx(1, 123 "I don't understand how to format " 124 "HAMMER version %d\n", 125 HammerVersion); 126 } 127 break; 128 default: 129 usage(); 130 break; 131 } 132 } 133 134 if (label == NULL) { 135 fprintf(stderr, 136 "newfs_hammer: A filesystem label must be specified\n"); 137 usage(); 138 } 139 140 if (HammerVersion < 0) { 141 size_t olen = sizeof(HammerVersion); 142 HammerVersion = HAMMER_VOL_VERSION_DEFAULT; 143 if (sysctlbyname("vfs.hammer.supported_version", 144 &HammerVersion, &olen, NULL, 0) == 0) { 145 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) { 146 HammerVersion = HAMMER_VOL_VERSION_WIP - 1; 147 fprintf(stderr, 148 "newfs_hammer: WARNING: HAMMER VFS " 149 "supports higher version than I " 150 "understand,\n" 151 "using version %d\n", 152 HammerVersion); 153 } 154 } else { 155 fprintf(stderr, 156 "newfs_hammer: WARNING: HAMMER VFS not " 157 "loaded, cannot get version info.\n" 158 "Using version %d\n", 159 HAMMER_VOL_VERSION_DEFAULT); 160 } 161 } 162 163 /* 164 * Collect volume information 165 */ 166 ac -= optind; 167 av += optind; 168 NumVolumes = ac; 169 RootVolNo = 0; 170 171 if (NumVolumes == 0) { 172 fprintf(stderr, 173 "newfs_hammer: You must specify at least one special file (volume)\n"); 174 exit(1); 175 } 176 177 if (NumVolumes > HAMMER_MAX_VOLUMES) { 178 fprintf(stderr, 179 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES); 180 exit(1); 181 } 182 183 total = 0; 184 for (i = 0; i < NumVolumes; ++i) { 185 vol = setup_volume(i, av[i], 1, O_RDWR); 186 187 /* 188 * Load up information on the volume and initialize 189 * its remaining fields. 190 */ 191 check_volume(vol); 192 total += vol->size; 193 } 194 195 /* 196 * Calculate defaults for the boot and memory area sizes. 197 */ 198 if (BootAreaSize == 0) { 199 BootAreaSize = HAMMER_BOOT_NOMBYTES; 200 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES) 201 BootAreaSize >>= 1; 202 if (BootAreaSize < HAMMER_BOOT_MINBYTES) 203 BootAreaSize = 0; 204 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) { 205 BootAreaSize = HAMMER_BOOT_MINBYTES; 206 } 207 if (MemAreaSize == 0) { 208 MemAreaSize = HAMMER_MEM_NOMBYTES; 209 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES) 210 MemAreaSize >>= 1; 211 if (MemAreaSize < HAMMER_MEM_MINBYTES) 212 MemAreaSize = 0; 213 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) { 214 MemAreaSize = HAMMER_MEM_MINBYTES; 215 } 216 217 /* 218 * Format the volumes. Format the root volume first so we can 219 * bootstrap the freemap. 220 */ 221 format_volume(get_volume(RootVolNo), NumVolumes, label, total); 222 for (i = 0; i < NumVolumes; ++i) { 223 if (i != RootVolNo) 224 format_volume(get_volume(i), NumVolumes, label, total); 225 } 226 227 /* 228 * Pre-size the blockmap layer1/layer2 infrastructure to the zone 229 * limit. If we do this the filesystem does not have to allocate 230 * new layer2 blocks which reduces the chances of the reblocker 231 * having to fallback to an extremely inefficient algorithm. 232 */ 233 vol = get_volume(RootVolNo); 234 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks; 235 vol->cache.modified = 1; 236 uuid_to_string(&Hammer_FSId, &fsidstr, &status); 237 238 printf("---------------------------------------------\n"); 239 printf("%d volume%s total size %s version %d\n", 240 NumVolumes, (NumVolumes == 1 ? "" : "s"), 241 sizetostr(total), HammerVersion); 242 printf("boot-area-size: %s\n", sizetostr(BootAreaSize)); 243 printf("memory-log-size: %s\n", sizetostr(MemAreaSize)); 244 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize)); 245 printf("total-pre-allocated: %s\n", 246 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK)); 247 printf("fsid: %s\n", fsidstr); 248 printf("\n"); 249 printf("NOTE: Please remember that you may have to manually set up a\n" 250 "cron(8) job to prune and reblock the filesystem regularly.\n" 251 "By default, the system automatically runs 'hammer cleanup'\n" 252 "on a nightly basis. The periodic.conf(5) variable\n" 253 "'daily_clean_hammer_enable' can be unset to disable this.\n" 254 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 255 if (total < 10*GIG) { 256 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you " 257 "really should not\n" 258 "try to format a HAMMER filesystem this small.\n"); 259 } 260 if (total < 50*GIG) { 261 printf("\nWARNING: HAMMER filesystems less than 50GB are " 262 "not recommended!\n" 263 "You may have to run 'hammer prune-everything' and " 264 "'hammer reblock'\n" 265 "quite often, even if using a nohistory mount.\n"); 266 } 267 flush_all_volumes(); 268 return(0); 269 } 270 271 static 272 void 273 usage(void) 274 { 275 fprintf(stderr, 276 "usage: newfs_hammer -L label [-f] [-b bootsize] [-m savesize] [-u undosize]\n" 277 " [-V version] special ...\n" 278 ); 279 exit(1); 280 } 281 282 /* 283 * Convert the size in bytes to a human readable string. 284 */ 285 static 286 const char * 287 sizetostr(off_t size) 288 { 289 static char buf[32]; 290 291 if (size < 1024 / 2) { 292 snprintf(buf, sizeof(buf), "%6.2f", (double)size); 293 } else if (size < 1024 * 1024 / 2) { 294 snprintf(buf, sizeof(buf), "%6.2fKB", 295 (double)size / 1024); 296 } else if (size < 1024 * 1024 * 1024LL / 2) { 297 snprintf(buf, sizeof(buf), "%6.2fMB", 298 (double)size / (1024 * 1024)); 299 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 300 snprintf(buf, sizeof(buf), "%6.2fGB", 301 (double)size / (1024 * 1024 * 1024LL)); 302 } else { 303 snprintf(buf, sizeof(buf), "%6.2fTB", 304 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 305 } 306 return(buf); 307 } 308 309 /* 310 * Convert a string to a 64 bit signed integer with various requirements. 311 */ 312 static int64_t 313 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 314 { 315 int64_t val; 316 char *ptr; 317 318 val = strtoll(str, &ptr, 0); 319 switch(*ptr) { 320 case 't': 321 case 'T': 322 val *= 1024; 323 /* fall through */ 324 case 'g': 325 case 'G': 326 val *= 1024; 327 /* fall through */ 328 case 'm': 329 case 'M': 330 val *= 1024; 331 /* fall through */ 332 case 'k': 333 case 'K': 334 val *= 1024; 335 break; 336 default: 337 errx(1, "Unknown suffix in number '%s'\n", str); 338 /* not reached */ 339 } 340 if (ptr[1]) { 341 errx(1, "Unknown suffix in number '%s'\n", str); 342 /* not reached */ 343 } 344 if (val < minval) { 345 errx(1, "Value too small: %s, min is %s\n", 346 str, sizetostr(minval)); 347 /* not reached */ 348 } 349 if (val > maxval) { 350 errx(1, "Value too large: %s, max is %s\n", 351 str, sizetostr(maxval)); 352 /* not reached */ 353 } 354 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 355 errx(1, "Value not power of 2: %s\n", str); 356 /* not reached */ 357 } 358 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 359 errx(1, "Value not an integral multiple of %dK: %s", 360 HAMMER_BUFSIZE / 1024, str); 361 /* not reached */ 362 } 363 return(val); 364 } 365 366 /* 367 * Generate a transaction id. Transaction ids are no longer time-based. 368 * Put the nail in the coffin by not making the first one time-based. 369 * 370 * We could start at 1 here but start at 2^32 to reserve a small domain for 371 * possible future use. 372 */ 373 static hammer_tid_t 374 createtid(void) 375 { 376 static hammer_tid_t lasttid; 377 378 if (lasttid == 0) 379 lasttid = 0x0000000100000000ULL; 380 return(lasttid++); 381 } 382 383 static u_int64_t 384 nowtime(void) 385 { 386 struct timeval tv; 387 u_int64_t xtime; 388 389 gettimeofday(&tv, NULL); 390 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 391 return(xtime); 392 } 393 394 /* 395 * Check basic volume characteristics. HAMMER filesystems use a minimum 396 * of a 16KB filesystem buffer size. 397 */ 398 static 399 void 400 check_volume(struct volume_info *vol) 401 { 402 struct partinfo pinfo; 403 struct stat st; 404 405 /* 406 * Get basic information about the volume 407 */ 408 vol->fd = open(vol->name, O_RDWR); 409 if (vol->fd < 0) 410 err(1, "Unable to open %s R+W", vol->name); 411 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) { 412 /* 413 * Allow the formatting of regular files as HAMMER volumes 414 */ 415 if (fstat(vol->fd, &st) < 0) 416 err(1, "Unable to stat %s", vol->name); 417 vol->size = st.st_size; 418 vol->type = "REGFILE"; 419 } else { 420 /* 421 * When formatting a block device as a HAMMER volume the 422 * sector size must be compatible. HAMMER uses 16384 byte 423 * filesystem buffers. 424 */ 425 if (pinfo.reserved_blocks) { 426 errx(1, "HAMMER cannot be placed in a partition " 427 "which overlaps the disklabel or MBR"); 428 } 429 if (pinfo.media_blksize > 16384 || 430 16384 % pinfo.media_blksize) { 431 errx(1, "A media sector size of %d is not supported", 432 pinfo.media_blksize); 433 } 434 435 vol->size = pinfo.media_size; 436 vol->type = "DEVICE"; 437 } 438 printf("Volume %d %s %-15s size %s\n", 439 vol->vol_no, vol->type, vol->name, 440 sizetostr(vol->size)); 441 442 /* 443 * Reserve space for (future) header junk, setup our poor-man's 444 * bigblock allocator. 445 */ 446 vol->vol_alloc = HAMMER_BUFSIZE * 16; 447 } 448 449 /* 450 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0. 451 */ 452 static 453 void 454 format_volume(struct volume_info *vol, int nvols, const char *label, 455 off_t total_size __unused) 456 { 457 struct volume_info *root_vol; 458 struct hammer_volume_ondisk *ondisk; 459 int64_t freeblks; 460 int64_t freebytes; 461 int i; 462 463 /* 464 * Initialize basic information in the on-disk volume structure. 465 */ 466 ondisk = vol->ondisk; 467 468 ondisk->vol_fsid = Hammer_FSId; 469 ondisk->vol_fstype = Hammer_FSType; 470 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label); 471 ondisk->vol_no = vol->vol_no; 472 ondisk->vol_count = nvols; 473 ondisk->vol_version = HammerVersion; 474 475 ondisk->vol_bot_beg = vol->vol_alloc; 476 vol->vol_alloc += BootAreaSize; 477 ondisk->vol_mem_beg = vol->vol_alloc; 478 vol->vol_alloc += MemAreaSize; 479 480 /* 481 * The remaining area is the zone 2 buffer allocation area. These 482 * buffers 483 */ 484 ondisk->vol_buf_beg = vol->vol_alloc; 485 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 486 487 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) { 488 errx(1, "volume %d %s is too small to hold the volume header", 489 vol->vol_no, vol->name); 490 } 491 492 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 493 HAMMER_BUFSIZE; 494 ondisk->vol_blocksize = HAMMER_BUFSIZE; 495 496 ondisk->vol_rootvol = RootVolNo; 497 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 498 499 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 500 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 501 (ondisk->vol_buf_end - ondisk->vol_buf_beg) & 502 ~HAMMER_LARGEBLOCK_MASK64); 503 504 /* 505 * Format the root volume. 506 */ 507 if (vol->vol_no == RootVolNo) { 508 /* 509 * Starting TID 510 */ 511 ondisk->vol0_next_tid = createtid(); 512 513 format_freemap(vol, 514 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]); 515 516 freeblks = initialize_freemap(vol); 517 ondisk->vol0_stat_freebigblocks = freeblks; 518 519 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64; 520 if (freebytes < 10*GIG && ForceOpt == 0) { 521 errx(1, "Cannot create a HAMMER filesystem less than " 522 "10GB unless you use -f. HAMMER filesystems\n" 523 "less than 50GB are not recommended\n"); 524 } 525 526 for (i = 8; i < HAMMER_MAX_ZONES; ++i) { 527 format_blockmap(&ondisk->vol0_blockmap[i], 528 HAMMER_ZONE_ENCODE(i, 0)); 529 } 530 format_undomap(ondisk); 531 532 ondisk->vol0_btree_root = format_root(label); 533 ++ondisk->vol0_stat_inodes; /* root inode */ 534 } else { 535 freeblks = initialize_freemap(vol); 536 root_vol = get_volume(RootVolNo); 537 root_vol->cache.modified = 1; 538 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 539 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 540 rel_volume(root_vol); 541 } 542 } 543 544 /* 545 * Format the root directory. 546 */ 547 static 548 hammer_off_t 549 format_root(const char *label) 550 { 551 hammer_off_t btree_off; 552 hammer_off_t pfsd_off; 553 hammer_off_t data_off; 554 hammer_tid_t create_tid; 555 hammer_node_ondisk_t bnode; 556 struct hammer_inode_data *idata; 557 hammer_pseudofs_data_t pfsd; 558 struct buffer_info *data_buffer1 = NULL; 559 struct buffer_info *data_buffer2 = NULL; 560 hammer_btree_elm_t elm; 561 u_int64_t xtime; 562 563 bnode = alloc_btree_element(&btree_off); 564 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1); 565 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 566 create_tid = createtid(); 567 xtime = nowtime(); 568 569 /* 570 * Populate the inode data and inode record for the root directory. 571 */ 572 idata->version = HAMMER_INODE_DATA_VERSION; 573 idata->mode = 0755; 574 idata->ctime = xtime; 575 idata->mtime = xtime; 576 idata->atime = xtime; 577 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 578 idata->size = 0; 579 idata->nlinks = 1; 580 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 581 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 582 if (HammerVersion >= HAMMER_VOL_VERSION_SIX) 583 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1; 584 585 pfsd->sync_low_tid = 1; 586 pfsd->sync_beg_tid = 0; 587 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 588 pfsd->shared_uuid = Hammer_FSId; 589 pfsd->unique_uuid = Hammer_FSId; 590 pfsd->reserved01 = 0; 591 pfsd->mirror_flags = 0; 592 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 593 594 /* 595 * Create the root of the B-Tree. The root is a leaf node so we 596 * do not have to worry about boundary elements. 597 */ 598 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD; 599 bnode->count = 2; 600 bnode->type = HAMMER_BTREE_TYPE_LEAF; 601 602 elm = &bnode->elms[0]; 603 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 604 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE; 605 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 606 elm->leaf.base.key = 0; 607 elm->leaf.base.create_tid = create_tid; 608 elm->leaf.base.delete_tid = 0; 609 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 610 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 611 elm->leaf.create_ts = (u_int32_t)time(NULL); 612 613 elm->leaf.data_offset = data_off; 614 elm->leaf.data_len = sizeof(*idata); 615 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 616 617 elm = &bnode->elms[1]; 618 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 619 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC; 620 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 621 elm->leaf.base.key = 0; 622 elm->leaf.base.create_tid = create_tid; 623 elm->leaf.base.delete_tid = 0; 624 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 625 elm->leaf.base.obj_type = 0; 626 elm->leaf.create_ts = (u_int32_t)time(NULL); 627 628 elm->leaf.data_offset = pfsd_off; 629 elm->leaf.data_len = sizeof(*pfsd); 630 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 631 632 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 633 634 return(btree_off); 635 } 636