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