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 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0); 223 if(errno == ENOENT) { 224 printf("%s %s (%s) does not support the TRIM " 225 "command\n", 226 vol->type, vol->name, sysctl_name); 227 usage(); 228 } 229 if(!trim_enabled) { 230 printf("Erase device option selected, but " 231 "sysctl (%s) is not enabled\n", sysctl_name); 232 usage(); 233 234 } 235 trim_volume(vol); 236 } 237 total += vol->size; 238 } 239 240 /* 241 * Calculate defaults for the boot and memory area sizes. 242 */ 243 avg_vol_size = total / nvols; 244 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size); 245 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size); 246 247 /* 248 * Format the volumes. Format the root volume first so we can 249 * bootstrap the freemap. 250 */ 251 format_volume(get_root_volume(), nvols, label); 252 for (i = 0; i < nvols; ++i) { 253 if (i != HAMMER_ROOT_VOLNO) 254 format_volume(get_volume(i), nvols, label); 255 } 256 257 /* 258 * Print information stored in the root volume header. 259 */ 260 vol = get_root_volume(); 261 uuid_to_string(&Hammer_FSId, &fsidstr, &status); 262 263 printf("---------------------------------------------\n"); 264 printf("%d volume%s total size %s version %d\n", 265 nvols, (nvols == 1 ? "" : "s"), 266 sizetostr(total), HammerVersion); 267 printf("root-volume: %s\n", vol->name); 268 printf("boot-area-size: %s\n", sizetostr(BootAreaSize)); 269 printf("memory-log-size: %s\n", sizetostr(MemAreaSize)); 270 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize)); 271 printf("total-pre-allocated: %s\n", 272 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK)); 273 printf("fsid: %s\n", fsidstr); 274 printf("\n"); 275 printf("NOTE: Please remember that you may have to manually set up a\n" 276 "cron(8) job to prune and reblock the filesystem regularly.\n" 277 "By default, the system automatically runs 'hammer cleanup'\n" 278 "on a nightly basis. The periodic.conf(5) variable\n" 279 "'daily_clean_hammer_enable' can be unset to disable this.\n" 280 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 281 if (total < 10*GIG) { 282 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you " 283 "really should not\n" 284 "try to format a HAMMER filesystem this small.\n"); 285 } 286 if (total < 50*GIG) { 287 printf("\nWARNING: HAMMER filesystems less than 50GB are " 288 "not recommended!\n" 289 "You may have to run 'hammer prune-everything' and " 290 "'hammer reblock'\n" 291 "quite often, even if using a nohistory mount.\n"); 292 } 293 flush_all_volumes(); 294 return(0); 295 } 296 297 static 298 void 299 usage(void) 300 { 301 fprintf(stderr, 302 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n" 303 " [-V version] special ...\n" 304 ); 305 exit(1); 306 } 307 308 /* 309 * Convert the size in bytes to a human readable string. 310 */ 311 static 312 const char * 313 sizetostr(off_t size) 314 { 315 static char buf[32]; 316 317 if (size < 1024 / 2) { 318 snprintf(buf, sizeof(buf), "%6.2f", (double)size); 319 } else if (size < 1024 * 1024 / 2) { 320 snprintf(buf, sizeof(buf), "%6.2fKB", 321 (double)size / 1024); 322 } else if (size < 1024 * 1024 * 1024LL / 2) { 323 snprintf(buf, sizeof(buf), "%6.2fMB", 324 (double)size / (1024 * 1024)); 325 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 326 snprintf(buf, sizeof(buf), "%6.2fGB", 327 (double)size / (1024 * 1024 * 1024LL)); 328 } else { 329 snprintf(buf, sizeof(buf), "%6.2fTB", 330 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 331 } 332 return(buf); 333 } 334 335 /* 336 * Convert a string to a 64 bit signed integer with various requirements. 337 */ 338 static int64_t 339 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 340 { 341 int64_t val; 342 char *ptr; 343 344 val = strtoll(str, &ptr, 0); 345 switch(*ptr) { 346 case 't': 347 case 'T': 348 val *= 1024; 349 /* fall through */ 350 case 'g': 351 case 'G': 352 val *= 1024; 353 /* fall through */ 354 case 'm': 355 case 'M': 356 val *= 1024; 357 /* fall through */ 358 case 'k': 359 case 'K': 360 val *= 1024; 361 break; 362 default: 363 errx(1, "Unknown suffix in number '%s'\n", str); 364 /* not reached */ 365 } 366 if (ptr[1]) { 367 errx(1, "Unknown suffix in number '%s'\n", str); 368 /* not reached */ 369 } 370 if (val < minval) { 371 errx(1, "Value too small: %s, min is %s\n", 372 str, sizetostr(minval)); 373 /* not reached */ 374 } 375 if (val > maxval) { 376 errx(1, "Value too large: %s, max is %s\n", 377 str, sizetostr(maxval)); 378 /* not reached */ 379 } 380 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 381 errx(1, "Value not power of 2: %s\n", str); 382 /* not reached */ 383 } 384 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 385 errx(1, "Value not an integral multiple of %dK: %s", 386 HAMMER_BUFSIZE / 1024, str); 387 /* not reached */ 388 } 389 return(val); 390 } 391 392 /* 393 * Generate a transaction id. Transaction ids are no longer time-based. 394 * Put the nail in the coffin by not making the first one time-based. 395 * 396 * We could start at 1 here but start at 2^32 to reserve a small domain for 397 * possible future use. 398 */ 399 static hammer_tid_t 400 createtid(void) 401 { 402 static hammer_tid_t lasttid; 403 404 if (lasttid == 0) 405 lasttid = 0x0000000100000000ULL; 406 return(lasttid++); 407 } 408 409 static uint64_t 410 nowtime(void) 411 { 412 struct timeval tv; 413 uint64_t xtime; 414 415 gettimeofday(&tv, NULL); 416 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 417 return(xtime); 418 } 419 420 /* 421 * TRIM the volume, but only if the backing store is a DEVICE 422 */ 423 static 424 void 425 trim_volume(struct volume_info *vol) 426 { 427 if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) { 428 off_t ioarg[2]; 429 430 /* 1MB offset to prevent destroying disk-reserved area */ 431 ioarg[0] = vol->device_offset; 432 ioarg[1] = vol->size; 433 434 printf("Trimming %s %s, sectors (%llu -%llu)\n", 435 vol->type, vol->name, 436 (unsigned long long)ioarg[0]/512, 437 (unsigned long long)ioarg[1]/512); 438 439 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) { 440 printf("Device trim failed\n"); 441 usage (); 442 } 443 } 444 } 445 446 /* 447 * Format a HAMMER volume. 448 */ 449 static 450 void 451 format_volume(struct volume_info *vol, int nvols, const char *label) 452 { 453 struct volume_info *root_vol; 454 struct hammer_volume_ondisk *ondisk; 455 int64_t freeblks; 456 int64_t freebytes; 457 int64_t vol_buf_size; 458 hammer_off_t vol_alloc; 459 int i; 460 461 /* 462 * Initialize basic information in the on-disk volume structure. 463 */ 464 ondisk = vol->ondisk; 465 466 ondisk->vol_fsid = Hammer_FSId; 467 ondisk->vol_fstype = Hammer_FSType; 468 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label); 469 ondisk->vol_no = vol->vol_no; 470 ondisk->vol_count = nvols; 471 ondisk->vol_version = HammerVersion; 472 473 /* 474 * Reserve space for (future) header junk, setup our poor-man's 475 * big-block allocator. 476 */ 477 vol_alloc = HAMMER_BUFSIZE * 16; /* 262144 */ 478 479 ondisk->vol_bot_beg = vol_alloc; 480 vol_alloc += BootAreaSize; 481 ondisk->vol_mem_beg = vol_alloc; 482 vol_alloc += MemAreaSize; 483 484 /* 485 * The remaining area is the zone 2 buffer allocation area. 486 */ 487 ondisk->vol_buf_beg = vol_alloc; 488 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 489 vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg; 490 491 if (vol_buf_size < 0) { 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_rootvol = HAMMER_ROOT_VOLNO; 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 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 502 503 /* 504 * Format the root volume. 505 */ 506 if (vol->vol_no == HAMMER_ROOT_VOLNO) { 507 /* 508 * Check freemap counts before formatting 509 */ 510 freeblks = count_freemap(vol); 511 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64; 512 if (freebytes < 10*GIG && ForceOpt == 0) { 513 errx(1, "Cannot create a HAMMER filesystem less than 10GB " 514 "unless you use -f\n(for the size of Volume %d). " 515 "HAMMER filesystems less than 50GB are not " 516 "recommended.\n", HAMMER_ROOT_VOLNO); 517 } 518 519 /* 520 * Starting TID 521 */ 522 ondisk->vol0_next_tid = createtid(); 523 524 /* 525 * Format freemap. vol0_stat_freebigblocks is 526 * the number of big-blocks available for anything 527 * other than freemap zone at this point. 528 */ 529 format_freemap(vol); 530 assert(ondisk->vol0_stat_freebigblocks == 0); 531 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol); 532 533 /* 534 * Format zones that are mapped to zone-2. 535 */ 536 for (i = 0; i < HAMMER_MAX_ZONES; ++i) { 537 if (hammer_is_zone2_mapped_index(i)) 538 format_blockmap(vol, i, 0); 539 } 540 541 /* 542 * Format undo zone. Formatting decrements 543 * vol0_stat_freebigblocks whenever a new big-block 544 * is allocated for undo zone. 545 */ 546 format_undomap(vol, &UndoBufferSize); 547 assert(ondisk->vol0_stat_bigblocks == 0); 548 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks; 549 550 /* 551 * Format the root directory. Formatting decrements 552 * vol0_stat_freebigblocks whenever a new big-block 553 * is allocated for required zones. 554 */ 555 ondisk->vol0_btree_root = format_root_directory(label); 556 ++ondisk->vol0_stat_inodes; /* root inode */ 557 558 rel_volume(vol); 559 } else { 560 freeblks = initialize_freemap(vol); 561 root_vol = get_root_volume(); 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_directory(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 /* 614 * Populate the PFS data for the root PFS. 615 */ 616 pfsd->sync_low_tid = 1; 617 pfsd->sync_beg_tid = 0; 618 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 619 pfsd->shared_uuid = Hammer_FSId; 620 pfsd->unique_uuid = Hammer_FSId; 621 pfsd->reserved01 = 0; 622 pfsd->mirror_flags = 0; 623 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 624 625 /* 626 * Create the root of the B-Tree. The root is a leaf node so we 627 * do not have to worry about boundary elements. 628 */ 629 bnode->count = 2; 630 bnode->type = HAMMER_BTREE_TYPE_LEAF; 631 632 /* 633 * Create the first node element for the inode. 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 /* 652 * Create the second node element for the PFS data. 653 */ 654 elm = &bnode->elms[1]; 655 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 656 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 657 HAMMER_LOCALIZE_MISC; 658 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 659 elm->leaf.base.key = 0; 660 elm->leaf.base.create_tid = create_tid; 661 elm->leaf.base.delete_tid = 0; 662 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 663 elm->leaf.base.obj_type = 0; 664 elm->leaf.create_ts = (uint32_t)time(NULL); 665 666 elm->leaf.data_offset = pfsd_off; 667 elm->leaf.data_len = sizeof(*pfsd); 668 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 669 670 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 671 672 rel_buffer(data_buffer0); 673 rel_buffer(data_buffer1); 674 rel_buffer(data_buffer2); 675 676 return(btree_off); 677 } 678