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