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