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