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 check_volume(struct volume_info *vol); 41 static void format_volume(struct volume_info *vol, int nvols,const char *label, 42 off_t total_size); 43 static hammer_off_t format_root(const char *label); 44 static u_int64_t nowtime(void); 45 static void usage(void); 46 47 static int ForceOpt = 0; 48 static int HammerVersion = -1; 49 static int Eflag = 0; 50 51 #define GIG (1024LL*1024*1024) 52 53 int 54 main(int ac, char **av) 55 { 56 u_int32_t status; 57 off_t total; 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_LARGEBLOCK_SIZE, 109 HAMMER_LARGEBLOCK_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 special file (volume)\n"); 177 exit(1); 178 } 179 180 if (NumVolumes > HAMMER_MAX_VOLUMES) { 181 fprintf(stderr, 182 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES); 183 exit(1); 184 } 185 186 total = 0; 187 for (i = 0; i < NumVolumes; ++i) { 188 vol = setup_volume(i, av[i], 1, O_RDWR); 189 190 /* 191 * Load up information on the volume and initialize 192 * its remaining fields. 193 */ 194 check_volume(vol); 195 if (Eflag) { 196 char sysctl_name[64]; 197 int trim_enabled = 0; 198 size_t olen = sizeof(trim_enabled); 199 char *dev_name = strdup(vol->name); 200 dev_name = strtok(dev_name + strlen("/dev/da"),"s"); 201 202 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", 203 dev_name); 204 errno=0; 205 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0); 206 if(errno == ENOENT) { 207 printf("Device:%s (%s) does not support the " 208 "TRIM command\n", vol->name,sysctl_name); 209 usage(); 210 } 211 if(!trim_enabled) { 212 printf("Erase device option selected, but " 213 "sysctl (%s) is not enabled\n", sysctl_name); 214 usage(); 215 216 } 217 trim_volume(vol); 218 } 219 total += vol->size; 220 } 221 222 /* 223 * Calculate defaults for the boot and memory area sizes. 224 */ 225 if (BootAreaSize == 0) { 226 BootAreaSize = HAMMER_BOOT_NOMBYTES; 227 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES) 228 BootAreaSize >>= 1; 229 if (BootAreaSize < HAMMER_BOOT_MINBYTES) 230 BootAreaSize = 0; 231 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) { 232 BootAreaSize = HAMMER_BOOT_MINBYTES; 233 } 234 if (MemAreaSize == 0) { 235 MemAreaSize = HAMMER_MEM_NOMBYTES; 236 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES) 237 MemAreaSize >>= 1; 238 if (MemAreaSize < HAMMER_MEM_MINBYTES) 239 MemAreaSize = 0; 240 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) { 241 MemAreaSize = HAMMER_MEM_MINBYTES; 242 } 243 244 /* 245 * Format the volumes. Format the root volume first so we can 246 * bootstrap the freemap. 247 */ 248 format_volume(get_volume(RootVolNo), NumVolumes, label, total); 249 for (i = 0; i < NumVolumes; ++i) { 250 if (i != RootVolNo) 251 format_volume(get_volume(i), NumVolumes, label, total); 252 } 253 254 /* 255 * Pre-size the blockmap layer1/layer2 infrastructure to the zone 256 * limit. If we do this the filesystem does not have to allocate 257 * new layer2 blocks which reduces the chances of the reblocker 258 * having to fallback to an extremely inefficient algorithm. 259 */ 260 vol = get_volume(RootVolNo); 261 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks; 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("boot-area-size: %s\n", sizetostr(BootAreaSize)); 270 printf("memory-log-size: %s\n", sizetostr(MemAreaSize)); 271 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize)); 272 printf("total-pre-allocated: %s\n", 273 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK)); 274 printf("fsid: %s\n", fsidstr); 275 printf("\n"); 276 printf("NOTE: Please remember that you may have to manually set up a\n" 277 "cron(8) job to prune and reblock the filesystem regularly.\n" 278 "By default, the system automatically runs 'hammer cleanup'\n" 279 "on a nightly basis. The periodic.conf(5) variable\n" 280 "'daily_clean_hammer_enable' can be unset to disable this.\n" 281 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 282 if (total < 10*GIG) { 283 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you " 284 "really should not\n" 285 "try to format a HAMMER filesystem this small.\n"); 286 } 287 if (total < 50*GIG) { 288 printf("\nWARNING: HAMMER filesystems less than 50GB are " 289 "not recommended!\n" 290 "You may have to run 'hammer prune-everything' and " 291 "'hammer reblock'\n" 292 "quite often, even if using a nohistory mount.\n"); 293 } 294 flush_all_volumes(); 295 return(0); 296 } 297 298 static 299 void 300 usage(void) 301 { 302 fprintf(stderr, 303 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n" 304 " [-V version] special ...\n" 305 ); 306 exit(1); 307 } 308 309 /* 310 * Convert the size in bytes to a human readable string. 311 */ 312 static 313 const char * 314 sizetostr(off_t size) 315 { 316 static char buf[32]; 317 318 if (size < 1024 / 2) { 319 snprintf(buf, sizeof(buf), "%6.2f", (double)size); 320 } else if (size < 1024 * 1024 / 2) { 321 snprintf(buf, sizeof(buf), "%6.2fKB", 322 (double)size / 1024); 323 } else if (size < 1024 * 1024 * 1024LL / 2) { 324 snprintf(buf, sizeof(buf), "%6.2fMB", 325 (double)size / (1024 * 1024)); 326 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 327 snprintf(buf, sizeof(buf), "%6.2fGB", 328 (double)size / (1024 * 1024 * 1024LL)); 329 } else { 330 snprintf(buf, sizeof(buf), "%6.2fTB", 331 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 332 } 333 return(buf); 334 } 335 336 /* 337 * Convert a string to a 64 bit signed integer with various requirements. 338 */ 339 static int64_t 340 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 341 { 342 int64_t val; 343 char *ptr; 344 345 val = strtoll(str, &ptr, 0); 346 switch(*ptr) { 347 case 't': 348 case 'T': 349 val *= 1024; 350 /* fall through */ 351 case 'g': 352 case 'G': 353 val *= 1024; 354 /* fall through */ 355 case 'm': 356 case 'M': 357 val *= 1024; 358 /* fall through */ 359 case 'k': 360 case 'K': 361 val *= 1024; 362 break; 363 default: 364 errx(1, "Unknown suffix in number '%s'\n", str); 365 /* not reached */ 366 } 367 if (ptr[1]) { 368 errx(1, "Unknown suffix in number '%s'\n", str); 369 /* not reached */ 370 } 371 if (val < minval) { 372 errx(1, "Value too small: %s, min is %s\n", 373 str, sizetostr(minval)); 374 /* not reached */ 375 } 376 if (val > maxval) { 377 errx(1, "Value too large: %s, max is %s\n", 378 str, sizetostr(maxval)); 379 /* not reached */ 380 } 381 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 382 errx(1, "Value not power of 2: %s\n", str); 383 /* not reached */ 384 } 385 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 386 errx(1, "Value not an integral multiple of %dK: %s", 387 HAMMER_BUFSIZE / 1024, str); 388 /* not reached */ 389 } 390 return(val); 391 } 392 393 /* 394 * Generate a transaction id. Transaction ids are no longer time-based. 395 * Put the nail in the coffin by not making the first one time-based. 396 * 397 * We could start at 1 here but start at 2^32 to reserve a small domain for 398 * possible future use. 399 */ 400 static hammer_tid_t 401 createtid(void) 402 { 403 static hammer_tid_t lasttid; 404 405 if (lasttid == 0) 406 lasttid = 0x0000000100000000ULL; 407 return(lasttid++); 408 } 409 410 static u_int64_t 411 nowtime(void) 412 { 413 struct timeval tv; 414 u_int64_t xtime; 415 416 gettimeofday(&tv, NULL); 417 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 418 return(xtime); 419 } 420 421 /* 422 * TRIM the volume, but only if the backing store is a DEVICE 423 */ 424 static 425 void 426 trim_volume(struct volume_info *vol) 427 { 428 if (strncmp(vol->type, "DEVICE", sizeof(vol->type)) == 0) { 429 off_t ioarg[2]; 430 431 /* 1MB offset to prevent destroying disk-reserved area */ 432 ioarg[0] = vol->device_offset; 433 ioarg[1] = vol->size; 434 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name, 435 (unsigned long long)ioarg[0]/512, 436 (unsigned long long)ioarg[1]/512); 437 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) { 438 printf("Device trim failed\n"); 439 usage (); 440 } 441 } 442 } 443 444 /* 445 * Check basic volume characteristics. HAMMER filesystems use a minimum 446 * of a 16KB filesystem buffer size. 447 */ 448 static 449 void 450 check_volume(struct volume_info *vol) 451 { 452 struct partinfo pinfo; 453 struct stat st; 454 455 /* 456 * Get basic information about the volume 457 */ 458 vol->fd = open(vol->name, O_RDWR); 459 if (vol->fd < 0) 460 err(1, "Unable to open %s R+W", vol->name); 461 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) { 462 /* 463 * Allow the formatting of regular files as HAMMER volumes 464 */ 465 if (fstat(vol->fd, &st) < 0) 466 err(1, "Unable to stat %s", vol->name); 467 vol->size = st.st_size; 468 vol->type = "REGFILE"; 469 470 if (Eflag) 471 errx(1,"Cannot TRIM regular file %s\n", vol->name); 472 473 } else { 474 /* 475 * When formatting a block device as a HAMMER volume the 476 * sector size must be compatible. HAMMER uses 16384 byte 477 * filesystem buffers. 478 */ 479 if (pinfo.reserved_blocks) { 480 errx(1, "HAMMER cannot be placed in a partition " 481 "which overlaps the disklabel or MBR"); 482 } 483 if (pinfo.media_blksize > 16384 || 484 16384 % pinfo.media_blksize) { 485 errx(1, "A media sector size of %d is not supported", 486 pinfo.media_blksize); 487 } 488 489 vol->size = pinfo.media_size; 490 vol->device_offset = pinfo.media_offset; 491 vol->type = "DEVICE"; 492 } 493 printf("Volume %d %s %-15s size %s\n", 494 vol->vol_no, vol->type, vol->name, 495 sizetostr(vol->size)); 496 497 /* 498 * Reserve space for (future) header junk, setup our poor-man's 499 * bigblock allocator. 500 */ 501 vol->vol_alloc = HAMMER_BUFSIZE * 16; 502 } 503 504 /* 505 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0. 506 */ 507 static 508 void 509 format_volume(struct volume_info *vol, int nvols, const char *label, 510 off_t total_size __unused) 511 { 512 struct volume_info *root_vol; 513 struct hammer_volume_ondisk *ondisk; 514 int64_t freeblks; 515 int64_t freebytes; 516 int i; 517 518 /* 519 * Initialize basic information in the on-disk volume structure. 520 */ 521 ondisk = vol->ondisk; 522 523 ondisk->vol_fsid = Hammer_FSId; 524 ondisk->vol_fstype = Hammer_FSType; 525 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label); 526 ondisk->vol_no = vol->vol_no; 527 ondisk->vol_count = nvols; 528 ondisk->vol_version = HammerVersion; 529 530 ondisk->vol_bot_beg = vol->vol_alloc; 531 vol->vol_alloc += BootAreaSize; 532 ondisk->vol_mem_beg = vol->vol_alloc; 533 vol->vol_alloc += MemAreaSize; 534 535 /* 536 * The remaining area is the zone 2 buffer allocation area. These 537 * buffers 538 */ 539 ondisk->vol_buf_beg = vol->vol_alloc; 540 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 541 542 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) { 543 errx(1, "volume %d %s is too small to hold the volume header", 544 vol->vol_no, vol->name); 545 } 546 547 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 548 HAMMER_BUFSIZE; 549 ondisk->vol_blocksize = HAMMER_BUFSIZE; 550 551 ondisk->vol_rootvol = RootVolNo; 552 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 553 554 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 555 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 556 (ondisk->vol_buf_end - ondisk->vol_buf_beg) & 557 ~HAMMER_LARGEBLOCK_MASK64); 558 559 /* 560 * Format the root volume. 561 */ 562 if (vol->vol_no == RootVolNo) { 563 /* 564 * Starting TID 565 */ 566 ondisk->vol0_next_tid = createtid(); 567 568 format_freemap(vol, 569 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]); 570 571 freeblks = initialize_freemap(vol); 572 ondisk->vol0_stat_freebigblocks = freeblks; 573 574 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64; 575 if (freebytes < 10*GIG && ForceOpt == 0) { 576 errx(1, "Cannot create a HAMMER filesystem less than " 577 "10GB unless you use -f. HAMMER filesystems\n" 578 "less than 50GB are not recommended\n"); 579 } 580 581 for (i = 8; i < HAMMER_MAX_ZONES; ++i) { 582 format_blockmap(&ondisk->vol0_blockmap[i], 583 HAMMER_ZONE_ENCODE(i, 0)); 584 } 585 format_undomap(ondisk); 586 587 ondisk->vol0_btree_root = format_root(label); 588 ++ondisk->vol0_stat_inodes; /* root inode */ 589 } else { 590 freeblks = initialize_freemap(vol); 591 root_vol = get_volume(RootVolNo); 592 root_vol->cache.modified = 1; 593 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 594 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 595 rel_volume(root_vol); 596 } 597 } 598 599 /* 600 * Format the root directory. 601 */ 602 static 603 hammer_off_t 604 format_root(const char *label) 605 { 606 hammer_off_t btree_off; 607 hammer_off_t pfsd_off; 608 hammer_off_t data_off; 609 hammer_tid_t create_tid; 610 hammer_node_ondisk_t bnode; 611 struct hammer_inode_data *idata; 612 hammer_pseudofs_data_t pfsd; 613 struct buffer_info *data_buffer1 = NULL; 614 struct buffer_info *data_buffer2 = NULL; 615 hammer_btree_elm_t elm; 616 u_int64_t xtime; 617 618 bnode = alloc_btree_element(&btree_off); 619 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1); 620 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 621 create_tid = createtid(); 622 xtime = nowtime(); 623 624 /* 625 * Populate the inode data and inode record for the root directory. 626 */ 627 idata->version = HAMMER_INODE_DATA_VERSION; 628 idata->mode = 0755; 629 idata->ctime = xtime; 630 idata->mtime = xtime; 631 idata->atime = xtime; 632 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 633 idata->size = 0; 634 idata->nlinks = 1; 635 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 636 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 637 if (HammerVersion >= HAMMER_VOL_VERSION_SIX) 638 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1; 639 640 pfsd->sync_low_tid = 1; 641 pfsd->sync_beg_tid = 0; 642 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 643 pfsd->shared_uuid = Hammer_FSId; 644 pfsd->unique_uuid = Hammer_FSId; 645 pfsd->reserved01 = 0; 646 pfsd->mirror_flags = 0; 647 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 648 649 /* 650 * Create the root of the B-Tree. The root is a leaf node so we 651 * do not have to worry about boundary elements. 652 */ 653 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD; 654 bnode->count = 2; 655 bnode->type = HAMMER_BTREE_TYPE_LEAF; 656 657 elm = &bnode->elms[0]; 658 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 659 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE; 660 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 661 elm->leaf.base.key = 0; 662 elm->leaf.base.create_tid = create_tid; 663 elm->leaf.base.delete_tid = 0; 664 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 665 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 666 elm->leaf.create_ts = (u_int32_t)time(NULL); 667 668 elm->leaf.data_offset = data_off; 669 elm->leaf.data_len = sizeof(*idata); 670 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 671 672 elm = &bnode->elms[1]; 673 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 674 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC; 675 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 676 elm->leaf.base.key = 0; 677 elm->leaf.base.create_tid = create_tid; 678 elm->leaf.base.delete_tid = 0; 679 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 680 elm->leaf.base.obj_type = 0; 681 elm->leaf.create_ts = (u_int32_t)time(NULL); 682 683 elm->leaf.data_offset = pfsd_off; 684 elm->leaf.data_len = sizeof(*pfsd); 685 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 686 687 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 688 689 return(btree_off); 690 } 691