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 off_t total_size); 45 static hammer_off_t format_root(const char *label); 46 static uint64_t nowtime(void); 47 static void usage(void); 48 49 static int ForceOpt = 0; 50 static int HammerVersion = -1; 51 52 #define GIG (1024LL*1024*1024) 53 54 int 55 main(int ac, char **av) 56 { 57 uint32_t status; 58 off_t total; 59 off_t avg_vol_size; 60 int ch; 61 int i; 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 NumVolumes = ac; 175 RootVolNo = 0; 176 177 if (NumVolumes == 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 (NumVolumes > 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 < NumVolumes; ++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 / NumVolumes; 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), NumVolumes, label, total); 250 for (i = 0; i < NumVolumes; ++i) { 251 if (i != RootVolNo) 252 format_volume(get_volume(i), NumVolumes, label, total); 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 NumVolumes, (NumVolumes == 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 off_t total_size __unused) 455 { 456 struct volume_info *root_vol; 457 struct hammer_volume_ondisk *ondisk; 458 int64_t freeblks; 459 int64_t freebytes; 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 ondisk->vol_bot_beg = vol->vol_alloc; 475 vol->vol_alloc += BootAreaSize; 476 ondisk->vol_mem_beg = vol->vol_alloc; 477 vol->vol_alloc += MemAreaSize; 478 479 /* 480 * The remaining area is the zone 2 buffer allocation area. 481 */ 482 ondisk->vol_buf_beg = vol->vol_alloc; 483 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 484 485 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) { 486 errx(1, "volume %d %s is too small to hold the volume header", 487 vol->vol_no, vol->name); 488 } 489 490 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 491 HAMMER_BUFSIZE; 492 ondisk->vol_blocksize = HAMMER_BUFSIZE; 493 494 ondisk->vol_rootvol = RootVolNo; 495 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 496 497 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 498 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 499 (ondisk->vol_buf_end - ondisk->vol_buf_beg) & 500 ~HAMMER_BIGBLOCK_MASK64); 501 502 /* 503 * Format the root volume. 504 */ 505 if (vol->vol_no == RootVolNo) { 506 /* 507 * Check freemap counts before formatting 508 */ 509 freeblks = count_freemap(vol); 510 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64; 511 if (freebytes < 10*GIG && ForceOpt == 0) { 512 errx(1, "Cannot create a HAMMER filesystem less than 10GB " 513 "unless you use -f\n(for the size of Volume %d). " 514 "HAMMER filesystems less than 50GB are not " 515 "recommended.\n", RootVolNo); 516 } 517 518 /* 519 * Starting TID 520 */ 521 ondisk->vol0_next_tid = createtid(); 522 523 /* 524 * Format freemap. vol0_stat_freebigblocks is 525 * the number of big-blocks available for anything 526 * other than freemap zone at this point. 527 */ 528 format_freemap(vol); 529 assert(ondisk->vol0_stat_freebigblocks == 0); 530 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol); 531 532 /* 533 * Format zones that are mapped to zone-2. 534 */ 535 for (i = 0; i < HAMMER_MAX_ZONES; ++i) { 536 if (hammer_is_zone2_mapped_index(i)) 537 format_blockmap(&ondisk->vol0_blockmap[i], i, 0); 538 } 539 540 /* 541 * Format undo zone. Formatting decrements 542 * vol0_stat_freebigblocks whenever a new big-block 543 * is allocated for undo zone. 544 */ 545 format_undomap(vol); 546 assert(ondisk->vol0_stat_bigblocks == 0); 547 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks; 548 549 /* 550 * Format the root directory. Formatting decrements 551 * vol0_stat_freebigblocks whenever a new big-block 552 * is allocated for required zones. 553 */ 554 ondisk->vol0_btree_root = format_root(label); 555 ++ondisk->vol0_stat_inodes; /* root inode */ 556 557 rel_volume(vol); 558 } else { 559 freeblks = initialize_freemap(vol); 560 root_vol = get_volume(RootVolNo); 561 root_vol->cache.modified = 1; 562 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 563 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 564 rel_volume(root_vol); 565 } 566 } 567 568 /* 569 * Format the root directory. 570 */ 571 static 572 hammer_off_t 573 format_root(const char *label) 574 { 575 hammer_off_t btree_off; 576 hammer_off_t pfsd_off; 577 hammer_off_t data_off; 578 hammer_tid_t create_tid; 579 hammer_node_ondisk_t bnode; 580 struct hammer_inode_data *idata; 581 hammer_pseudofs_data_t pfsd; 582 struct buffer_info *data_buffer0 = NULL; 583 struct buffer_info *data_buffer1 = NULL; 584 struct buffer_info *data_buffer2 = NULL; 585 hammer_btree_elm_t elm; 586 uint64_t xtime; 587 588 /* 589 * Allocate zero-filled root btree node, inode and pfs 590 */ 591 bnode = alloc_btree_element(&btree_off, &data_buffer0); 592 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1); 593 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 594 create_tid = createtid(); 595 xtime = nowtime(); 596 597 /* 598 * Populate the inode data and inode record for the root directory. 599 */ 600 idata->version = HAMMER_INODE_DATA_VERSION; 601 idata->mode = 0755; 602 idata->ctime = xtime; 603 idata->mtime = xtime; 604 idata->atime = xtime; 605 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 606 idata->size = 0; 607 idata->nlinks = 1; 608 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 609 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 610 if (HammerVersion >= HAMMER_VOL_VERSION_SIX) 611 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1; 612 613 pfsd->sync_low_tid = 1; 614 pfsd->sync_beg_tid = 0; 615 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 616 pfsd->shared_uuid = Hammer_FSId; 617 pfsd->unique_uuid = Hammer_FSId; 618 pfsd->reserved01 = 0; 619 pfsd->mirror_flags = 0; 620 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 621 622 /* 623 * Create the root of the B-Tree. The root is a leaf node so we 624 * do not have to worry about boundary elements. 625 */ 626 bnode->count = 2; 627 bnode->type = HAMMER_BTREE_TYPE_LEAF; 628 629 elm = &bnode->elms[0]; 630 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 631 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 632 HAMMER_LOCALIZE_INODE; 633 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 634 elm->leaf.base.key = 0; 635 elm->leaf.base.create_tid = create_tid; 636 elm->leaf.base.delete_tid = 0; 637 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 638 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 639 elm->leaf.create_ts = (uint32_t)time(NULL); 640 641 elm->leaf.data_offset = data_off; 642 elm->leaf.data_len = sizeof(*idata); 643 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 644 645 elm = &bnode->elms[1]; 646 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 647 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 648 HAMMER_LOCALIZE_MISC; 649 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 650 elm->leaf.base.key = 0; 651 elm->leaf.base.create_tid = create_tid; 652 elm->leaf.base.delete_tid = 0; 653 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 654 elm->leaf.base.obj_type = 0; 655 elm->leaf.create_ts = (uint32_t)time(NULL); 656 657 elm->leaf.data_offset = pfsd_off; 658 elm->leaf.data_len = sizeof(*pfsd); 659 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 660 661 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 662 663 return(btree_off); 664 } 665