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