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 "hammer_util.h" 36 37 #include <sys/sysctl.h> 38 39 #include <bus/cam/scsi/scsi_daio.h> 40 41 static int64_t getsize(const char *str, int pw); 42 static int trim_volume(volume_info_t volume); 43 static void format_volume(volume_info_t volume, 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 print_volume(const volume_info_t volume); 47 static void usage(int exit_code); 48 static void test_header_junk_size(int64_t size); 49 static void test_boot_area_size(int64_t size); 50 static void test_memory_log_size(int64_t size); 51 static void test_undo_buffer_size(int64_t size); 52 53 static int ForceOpt; 54 static int64_t HeaderJunkSize = -1; 55 static int64_t BootAreaSize = -1; 56 static int64_t MemoryLogSize = -1; 57 static int64_t UndoBufferSize; 58 59 #define GIG (1024LL*1024*1024) 60 61 int 62 main(int ac, char **av) 63 { 64 off_t total; 65 off_t avg_vol_size; 66 int ch; 67 int i; 68 int nvols; 69 int eflag = 0; 70 const char *label = NULL; 71 volume_info_t volume; 72 73 /* 74 * Sanity check basic filesystem structures. No cookies for us 75 * if it gets broken! 76 */ 77 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE); 78 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_MIN_VOL_JUNK); 79 assert(sizeof(struct hammer_blockmap_layer1) == 32); 80 assert(sizeof(struct hammer_blockmap_layer2) == 16); 81 82 /* 83 * Parse arguments 84 */ 85 while ((ch = getopt(ac, av, "dfEL:j:b:m:u:hC:V:")) != -1) { 86 switch(ch) { 87 case 'd': 88 ++DebugOpt; 89 break; 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 'j': /* Not mentioned in newfs_hammer(8) */ 100 HeaderJunkSize = getsize(optarg, 2); 101 test_header_junk_size(HeaderJunkSize); 102 break; 103 case 'b': 104 BootAreaSize = getsize(optarg, 2); 105 test_boot_area_size(BootAreaSize); 106 break; 107 case 'm': 108 MemoryLogSize = getsize(optarg, 2); 109 test_memory_log_size(MemoryLogSize); 110 break; 111 case 'u': 112 UndoBufferSize = getsize(optarg, 2); 113 test_undo_buffer_size(UndoBufferSize); 114 break; 115 case 'h': 116 usage(0); 117 /* not reached */ 118 break; 119 case 'C': 120 if (hammer_parse_cache_size(optarg) == -1) { 121 usage(1); 122 /* not reached */ 123 } 124 break; 125 case 'V': 126 HammerVersion = strtol(optarg, NULL, 0); 127 if (HammerVersion < HAMMER_VOL_VERSION_MIN || 128 HammerVersion >= HAMMER_VOL_VERSION_WIP) { 129 errx(1, 130 "I don't understand how to format " 131 "HAMMER version %d", 132 HammerVersion); 133 /* not reached */ 134 } 135 break; 136 default: 137 usage(1); 138 /* not reached */ 139 break; 140 } 141 } 142 ac -= optind; 143 av += optind; 144 nvols = ac; 145 146 if (HammerVersion == (uint32_t)-1) { 147 size_t olen = sizeof(HammerVersion); 148 HammerVersion = HAMMER_VOL_VERSION_DEFAULT; 149 150 if (sysctlbyname("vfs.hammer.supported_version", 151 &HammerVersion, &olen, NULL, 0)) { 152 hwarn("HAMMER VFS not loaded, cannot get version info, " 153 "using version %d", 154 HammerVersion); 155 } else if (HammerVersion >= HAMMER_VOL_VERSION_WIP) { 156 HammerVersion = HAMMER_VOL_VERSION_WIP - 1; 157 hwarnx("HAMMER VFS supports higher version than " 158 "I understand, using version %d", 159 HammerVersion); 160 } 161 } 162 163 if (nvols == 0) { 164 errx(1, "You must specify at least one special file (volume)"); 165 /* not reached */ 166 } 167 if (nvols > HAMMER_MAX_VOLUMES) { 168 errx(1, "The maximum number of volumes is %d", 169 HAMMER_MAX_VOLUMES); 170 /* not reached */ 171 } 172 173 if (label == NULL) { 174 hwarnx("A filesystem label must be specified"); 175 usage(1); 176 /* not reached */ 177 } 178 179 /* 180 * Generate a filesystem id and lookup the filesystem type 181 */ 182 hammer_uuid_create(&Hammer_FSId); 183 if (hammer_uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING)) { 184 errx(1, "uuids file does not have the DragonFly " 185 "HAMMER filesystem type"); 186 /* not reached */ 187 } 188 189 total = 0; 190 for (i = 0; i < nvols; ++i) { 191 volume = init_volume(av[i], O_RDWR, i); 192 printf("Volume %d %s %-15s size %s\n", 193 volume->vol_no, volume->type, volume->name, 194 sizetostr(volume->size)); 195 196 if (eflag) { 197 if (trim_volume(volume) == -1 && ForceOpt == 0) { 198 errx(1, "Use -f option to proceed"); 199 /* not reached */ 200 } 201 } 202 total += volume->size; 203 } 204 205 /* 206 * Reserve space for (future) header junk, setup our poor-man's 207 * big-block allocator. Note that the header junk space includes 208 * volume header which is 1928 bytes. 209 */ 210 if (HeaderJunkSize == -1) 211 HeaderJunkSize = HAMMER_VOL_JUNK_SIZE; 212 else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk)) 213 HeaderJunkSize = sizeof(struct hammer_volume_ondisk); 214 HeaderJunkSize = HAMMER_BUFSIZE_DOALIGN(HeaderJunkSize); 215 216 /* 217 * Calculate defaults for the boot area and memory log sizes, 218 * only if not specified by -b or -m option. 219 */ 220 avg_vol_size = total / nvols; 221 if (BootAreaSize == -1) 222 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size); 223 if (MemoryLogSize == -1) 224 MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size); 225 226 /* 227 * Format the volumes. Format the root volume first so we can 228 * bootstrap the freemap. 229 */ 230 format_volume(get_root_volume(), nvols, label); 231 for (i = 0; i < nvols; ++i) { 232 if (i != HAMMER_ROOT_VOLNO) 233 format_volume(get_volume(i), nvols, label); 234 } 235 236 print_volume(get_root_volume()); 237 238 flush_all_volumes(); 239 return(0); 240 } 241 242 static 243 void 244 print_volume(const volume_info_t volume) 245 { 246 hammer_volume_ondisk_t ondisk; 247 hammer_blockmap_t blockmap; 248 hammer_off_t total = 0; 249 int i, nvols; 250 const char *name = NULL; 251 char *fsidstr; 252 253 ondisk = volume->ondisk; 254 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX]; 255 256 nvols = ondisk->vol_count; 257 for (i = 0; i < nvols; ++i) { 258 volume_info_t p = get_volume(i); 259 total += p->size; 260 if (p->vol_no == HAMMER_ROOT_VOLNO) { 261 assert(name == NULL); 262 name = p->name; 263 } 264 } 265 266 hammer_uuid_to_string(&Hammer_FSId, &fsidstr); 267 268 printf("---------------------------------------------\n"); 269 printf("HAMMER version %d\n", HammerVersion); 270 printf("%d volume%s total size %s\n", 271 nvols, (nvols == 1 ? "" : "s"), sizetostr(total)); 272 printf("root-volume: %s\n", name); 273 if (DebugOpt) { 274 printf("header-junk-size: %s\n", 275 sizetostr(ondisk->vol_bot_beg)); 276 } 277 printf("boot-area-size: %s\n", 278 sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg)); 279 printf("memory-log-size: %s\n", 280 sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg)); 281 printf("undo-buffer-size: %s\n", 282 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset))); 283 printf("total-pre-allocated: %s\n", 284 sizetostr(HAMMER_OFF_SHORT_ENCODE(volume->vol_free_off))); 285 printf("fsid: %s\n", fsidstr); 286 printf("\n"); 287 printf("NOTE: Please remember that you may have to manually set up a\n" 288 "cron(8) job to prune and reblock the filesystem regularly.\n" 289 "By default, the system automatically runs 'hammer cleanup'\n" 290 "on a nightly basis. The periodic.conf(5) variable\n" 291 "'daily_clean_hammer_enable' can be unset to disable this.\n" 292 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 293 if (total < 10*GIG) { 294 printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, " 295 "you really should not\n" 296 "try to format a HAMMER filesystem this small.\n", 297 sizetostr(HAMMER_BIGBLOCK_SIZE * 298 HAMMER_MIN_UNDO_BIGBLOCKS)); 299 } 300 if (total < 50*GIG) { 301 printf("\nWARNING: HAMMER filesystems less than 50GB are " 302 "not recommended!\n" 303 "You may have to run 'hammer prune-everything' and " 304 "'hammer reblock'\n" 305 "quite often, even if using a nohistory mount.\n"); 306 } 307 } 308 309 static 310 void 311 usage(int exit_code) 312 { 313 fprintf(stderr, 314 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n" 315 " [-C cachesize[:readahead]] [-V version] special ...\n" 316 ); 317 exit(exit_code); 318 } 319 320 static 321 void 322 test_header_junk_size(int64_t size) 323 { 324 if (size < HAMMER_MIN_VOL_JUNK) { 325 if (ForceOpt == 0) { 326 errx(1, "The minimum header junk size is %s", 327 sizetostr(HAMMER_MIN_VOL_JUNK)); 328 /* not reached */ 329 } else { 330 hwarnx("You have specified header junk size less than %s", 331 sizetostr(HAMMER_MIN_VOL_JUNK)); 332 } 333 } else if (size > HAMMER_MAX_VOL_JUNK) { 334 errx(1, "The maximum header junk size is %s", 335 sizetostr(HAMMER_MAX_VOL_JUNK)); 336 /* not reached */ 337 } 338 } 339 340 static 341 void 342 test_boot_area_size(int64_t size) 343 { 344 if (size < HAMMER_BOOT_MINBYTES) { 345 if (ForceOpt == 0) { 346 errx(1, "The minimum boot area size is %s", 347 sizetostr(HAMMER_BOOT_MINBYTES)); 348 /* not reached */ 349 } else { 350 hwarnx("You have specified boot area size less than %s", 351 sizetostr(HAMMER_BOOT_MINBYTES)); 352 } 353 } else if (size > HAMMER_BOOT_MAXBYTES) { 354 errx(1, "The maximum boot area size is %s", 355 sizetostr(HAMMER_BOOT_MAXBYTES)); 356 /* not reached */ 357 } 358 } 359 360 static 361 void 362 test_memory_log_size(int64_t size) 363 { 364 if (size < HAMMER_MEM_MINBYTES) { 365 if (ForceOpt == 0) { 366 errx(1, "The minimum memory log size is %s", 367 sizetostr(HAMMER_MEM_MINBYTES)); 368 /* not reached */ 369 } else { 370 hwarnx("You have specified memory log size less than %s", 371 sizetostr(HAMMER_MEM_MINBYTES)); 372 } 373 } else if (size > HAMMER_MEM_MAXBYTES) { 374 errx(1, "The maximum memory log size is %s", 375 sizetostr(HAMMER_MEM_MAXBYTES)); 376 /* not reached */ 377 } 378 } 379 380 static 381 void 382 test_undo_buffer_size(int64_t size) 383 { 384 int64_t minbuf, maxbuf; 385 386 minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS; 387 maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS; 388 389 if (size < minbuf) { 390 if (ForceOpt == 0) { 391 errx(1, "The minimum UNDO/REDO FIFO size is %s", 392 sizetostr(minbuf)); 393 /* not reached */ 394 } else { 395 hwarnx("You have specified an UNDO/REDO FIFO size less " 396 "than %s, which may lead to VFS panics", 397 sizetostr(minbuf)); 398 } 399 } else if (size > maxbuf) { 400 errx(1, "The maximum UNDO/REDO FIFO size is %s", 401 sizetostr(maxbuf)); 402 /* not reached */ 403 } 404 } 405 406 /* 407 * Convert a string to a 64 bit signed integer with various requirements. 408 */ 409 static 410 int64_t 411 getsize(const char *str, int powerof2) 412 { 413 int64_t val; 414 char *ptr; 415 416 val = strtoll(str, &ptr, 0); 417 switch(*ptr) { 418 case 't': 419 case 'T': 420 val *= 1024; 421 /* fall through */ 422 case 'g': 423 case 'G': 424 val *= 1024; 425 /* fall through */ 426 case 'm': 427 case 'M': 428 val *= 1024; 429 /* fall through */ 430 case 'k': 431 case 'K': 432 val *= 1024; 433 break; 434 default: 435 errx(1, "Unknown suffix in number '%s'", str); 436 /* not reached */ 437 } 438 439 if (ptr[1]) { 440 errx(1, "Unknown suffix in number '%s'", str); 441 /* not reached */ 442 } 443 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 444 errx(1, "Value not power of 2: %s", str); 445 /* not reached */ 446 } 447 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 448 errx(1, "Value not an integral multiple of %dK: %s", 449 HAMMER_BUFSIZE / 1024, str); 450 /* not reached */ 451 } 452 return(val); 453 } 454 455 /* 456 * Generate a transaction id. Transaction ids are no longer time-based. 457 * Put the nail in the coffin by not making the first one time-based. 458 * 459 * We could start at 1 here but start at 2^32 to reserve a small domain for 460 * possible future use. 461 */ 462 static 463 hammer_tid_t 464 createtid(void) 465 { 466 static hammer_tid_t lasttid; 467 468 if (lasttid == 0) 469 lasttid = 0x0000000100000000ULL; 470 return(lasttid++); 471 } 472 473 static 474 uint64_t 475 nowtime(void) 476 { 477 struct timeval tv; 478 uint64_t xtime; 479 480 gettimeofday(&tv, NULL); 481 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 482 return(xtime); 483 } 484 485 /* 486 * TRIM the volume, but only if the backing store is not a regular file 487 */ 488 static 489 int 490 trim_volume(volume_info_t volume) 491 { 492 size_t olen; 493 char *dev_name, *p; 494 char sysctl_name[64]; 495 int trim_enabled; 496 off_t ioarg[2]; 497 498 if (is_regfile(volume)) { 499 hwarnx("Cannot TRIM regular file %s", volume->name); 500 return(-1); 501 } 502 if (strncmp(volume->name, "/dev/da", 7)) { 503 hwarnx("%s does not support the TRIM command", volume->name); 504 return(-1); 505 } 506 507 /* Extract a number from /dev/da?s? */ 508 dev_name = strdup(volume->name); 509 p = strtok(dev_name + strlen("/dev/da"), "s"); 510 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p); 511 free(dev_name); 512 513 trim_enabled = 0; 514 olen = sizeof(trim_enabled); 515 516 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) { 517 hwarnx("%s (%s) does not support the TRIM command", 518 volume->name, sysctl_name); 519 return(-1); 520 } 521 if (!trim_enabled) { 522 hwarnx("Erase device option selected, but sysctl (%s) " 523 "is not enabled", sysctl_name); 524 return(-1); 525 } 526 527 ioarg[0] = volume->device_offset; 528 ioarg[1] = volume->size; 529 530 printf("Trimming %s %s, sectors %llu-%llu\n", 531 volume->type, volume->name, 532 (unsigned long long)ioarg[0] / 512, 533 (unsigned long long)ioarg[1] / 512); 534 535 if (ioctl(volume->fd, DAIOCTRIM, ioarg) == -1) { 536 err(1, "Trimming %s failed", volume->name); 537 /* not reached */ 538 } 539 540 return(0); 541 } 542 543 /* 544 * Format a HAMMER volume. 545 */ 546 static 547 void 548 format_volume(volume_info_t volume, int nvols, const char *label) 549 { 550 volume_info_t root_vol; 551 hammer_volume_ondisk_t ondisk; 552 int64_t freeblks; 553 int64_t freebytes; 554 int64_t vol_buf_size; 555 hammer_off_t vol_alloc; 556 int i; 557 558 /* 559 * Initialize basic information in the on-disk volume structure. 560 */ 561 ondisk = volume->ondisk; 562 563 ondisk->vol_fsid = Hammer_FSId; 564 ondisk->vol_fstype = Hammer_FSType; 565 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label); 566 ondisk->vol_no = volume->vol_no; 567 ondisk->vol_count = nvols; 568 ondisk->vol_version = HammerVersion; 569 570 vol_alloc = HeaderJunkSize; 571 ondisk->vol_bot_beg = vol_alloc; 572 vol_alloc += BootAreaSize; 573 ondisk->vol_mem_beg = vol_alloc; 574 vol_alloc += MemoryLogSize; 575 576 /* 577 * The remaining area is the zone 2 buffer allocation area. 578 */ 579 ondisk->vol_buf_beg = vol_alloc; 580 ondisk->vol_buf_end = volume->size & ~(int64_t)HAMMER_BUFMASK; 581 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk); 582 583 if (vol_buf_size < (int64_t)sizeof(*ondisk)) { 584 errx(1, "volume %d %s is too small to hold the volume header", 585 volume->vol_no, volume->name); 586 /* not reached */ 587 } 588 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) { 589 errx(1, "volume %d %s is too large", volume->vol_no, volume->name); 590 /* not reached */ 591 } 592 593 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO; 594 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 595 596 volume->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0); 597 volume->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 598 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64); 599 600 /* 601 * Format the root volume. 602 */ 603 if (volume->vol_no == HAMMER_ROOT_VOLNO) { 604 /* 605 * Check freemap counts before formatting 606 */ 607 freeblks = count_freemap(volume); 608 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64; 609 if (freebytes < 10*GIG && ForceOpt == 0) { 610 errx(1, "Cannot create a HAMMER filesystem less than 10GB " 611 "unless you use -f\n(for the size of Volume %d). " 612 "HAMMER filesystems less than 50GB are not " 613 "recommended.", HAMMER_ROOT_VOLNO); 614 /* not reached */ 615 } 616 617 /* 618 * Starting TID 619 */ 620 ondisk->vol0_next_tid = createtid(); 621 622 /* 623 * Format freemap. vol0_stat_freebigblocks is 624 * the number of big-blocks available for anything 625 * other than freemap zone at this point. 626 */ 627 format_freemap(volume); 628 assert(ondisk->vol0_stat_freebigblocks == 0); 629 ondisk->vol0_stat_freebigblocks = initialize_freemap(volume); 630 631 /* 632 * Format zones that are mapped to zone-2. 633 */ 634 for (i = 0; i < HAMMER_MAX_ZONES; ++i) { 635 if (hammer_is_index_record(i)) 636 format_blockmap(volume, i, 0); 637 } 638 639 /* 640 * Format undo zone. Formatting decrements 641 * vol0_stat_freebigblocks whenever a new big-block 642 * is allocated for undo zone. 643 */ 644 format_undomap(volume, &UndoBufferSize); 645 assert(ondisk->vol0_stat_bigblocks == 0); 646 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks; 647 648 /* 649 * Format the root directory. Formatting decrements 650 * vol0_stat_freebigblocks whenever a new big-block 651 * is allocated for required zones. 652 */ 653 ondisk->vol0_btree_root = format_root_directory(label); 654 ++ondisk->vol0_stat_inodes; /* root inode */ 655 } else { 656 freeblks = initialize_freemap(volume); 657 root_vol = get_root_volume(); 658 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 659 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 660 } 661 } 662 663 /* 664 * Format the root directory. 665 */ 666 static 667 hammer_off_t 668 format_root_directory(const char *label) 669 { 670 hammer_off_t btree_off; 671 hammer_off_t idata_off; 672 hammer_off_t pfsd_off; 673 hammer_tid_t create_tid; 674 hammer_node_ondisk_t bnode; 675 hammer_inode_data_t idata; 676 hammer_pseudofs_data_t pfsd; 677 buffer_info_t data_buffer0 = NULL; 678 buffer_info_t data_buffer1 = NULL; 679 buffer_info_t data_buffer2 = NULL; 680 hammer_btree_elm_t elm; 681 uint64_t xtime; 682 683 /* 684 * Allocate zero-filled root btree node, inode and pfs 685 */ 686 bnode = alloc_btree_node(&btree_off, &data_buffer0); 687 idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1); 688 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 689 create_tid = createtid(); 690 xtime = nowtime(); 691 692 /* 693 * Populate the inode data and inode record for the root directory. 694 */ 695 idata->version = HAMMER_INODE_DATA_VERSION; 696 idata->mode = 0755; 697 idata->ctime = xtime; 698 idata->mtime = xtime; 699 idata->atime = xtime; 700 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 701 idata->size = 0; 702 idata->nlinks = 1; 703 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 704 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 705 if (HammerVersion >= HAMMER_VOL_VERSION_SIX) 706 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1; 707 708 /* 709 * Populate the PFS data for the root PFS. 710 */ 711 pfsd->sync_low_tid = 1; 712 pfsd->sync_beg_tid = 0; 713 pfsd->sync_end_tid = 0; /* overridden by vol0_next_tid on root PFS */ 714 pfsd->shared_uuid = Hammer_FSId; 715 pfsd->unique_uuid = Hammer_FSId; 716 pfsd->mirror_flags = 0; 717 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 718 719 /* 720 * Create the root of the B-Tree. The root is a leaf node so we 721 * do not have to worry about boundary elements. 722 */ 723 bnode->parent = 0; /* no parent */ 724 bnode->count = 2; 725 bnode->type = HAMMER_BTREE_TYPE_LEAF; 726 bnode->mirror_tid = 0; 727 728 /* 729 * Create the first node element for the inode. 730 */ 731 elm = &bnode->elms[0]; 732 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 733 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 734 HAMMER_LOCALIZE_INODE; 735 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 736 elm->leaf.base.key = 0; 737 elm->leaf.base.create_tid = create_tid; 738 elm->leaf.base.delete_tid = 0; 739 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 740 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 741 elm->leaf.create_ts = (uint32_t)time(NULL); 742 743 elm->leaf.data_offset = idata_off; 744 elm->leaf.data_len = sizeof(*idata); 745 hammer_crc_set_leaf(HammerVersion, idata, &elm->leaf); 746 747 /* 748 * Create the second node element for the PFS data. 749 * This is supposed to be a record part of the root ip (inode), 750 * so it should have the same obj_type value as above. 751 */ 752 elm = &bnode->elms[1]; 753 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 754 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION | 755 HAMMER_LOCALIZE_MISC; 756 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 757 elm->leaf.base.key = 0; 758 elm->leaf.base.create_tid = create_tid; 759 elm->leaf.base.delete_tid = 0; 760 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 761 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 762 elm->leaf.create_ts = (uint32_t)time(NULL); 763 764 elm->leaf.data_offset = pfsd_off; 765 elm->leaf.data_len = sizeof(*pfsd); 766 hammer_crc_set_leaf(HammerVersion, pfsd, &elm->leaf); 767 768 hammer_crc_set_btree(HammerVersion, bnode); 769 770 rel_buffer(data_buffer0); 771 rel_buffer(data_buffer1); 772 rel_buffer(data_buffer2); 773 774 return(btree_off); 775 } 776