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 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.6 2007/11/19 00:53:39 dillon Exp $ 35 */ 36 37 #include "newfs_hammer.h" 38 39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw); 40 static const char *sizetostr(off_t size); 41 static void check_volume(struct volume_info *vol); 42 static void format_volume(struct volume_info *vol, int nvols,const char *label); 43 static int32_t format_cluster(struct volume_info *vol, int isroot); 44 static void format_root(struct cluster_info *cluster); 45 static void usage(void); 46 47 struct hammer_alist_config Buf_alist_config; 48 struct hammer_alist_config Vol_normal_alist_config; 49 struct hammer_alist_config Vol_super_alist_config; 50 struct hammer_alist_config Supercl_alist_config; 51 struct hammer_alist_config Clu_master_alist_config; 52 struct hammer_alist_config Clu_slave_alist_config; 53 uuid_t Hammer_FSType; 54 uuid_t Hammer_FSId; 55 int32_t ClusterSize; 56 int UsingSuperClusters; 57 int NumVolumes; 58 struct volume_info *VolBase; 59 60 int 61 main(int ac, char **av) 62 { 63 int i; 64 int ch; 65 u_int32_t status; 66 off_t total; 67 int64_t max_volume_size; 68 const char *label = NULL; 69 70 /* 71 * Sanity check basic filesystem structures. No cookies for us 72 * if it gets broken! 73 */ 74 assert(sizeof(struct hammer_almeta) == HAMMER_ALMETA_SIZE); 75 assert(sizeof(struct hammer_fsbuf_head) == HAMMER_FSBUF_HEAD_SIZE); 76 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE); 77 assert(sizeof(struct hammer_cluster_ondisk) <= HAMMER_BUFSIZE); 78 assert(sizeof(struct hammer_fsbuf_data) == HAMMER_BUFSIZE); 79 assert(sizeof(struct hammer_fsbuf_recs) == HAMMER_BUFSIZE); 80 assert(sizeof(struct hammer_fsbuf_btree) == HAMMER_BUFSIZE); 81 assert(sizeof(union hammer_fsbuf_ondisk) == HAMMER_BUFSIZE); 82 83 /* 84 * Generate a filesysem id and lookup the filesystem type 85 */ 86 uuidgen(&Hammer_FSId, 1); 87 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status); 88 if (status != uuid_s_ok) { 89 errx(1, "uuids file does not have the DragonFly " 90 "HAMMER filesystem type"); 91 } 92 93 /* 94 * Initialize the alist templates we will be using 95 */ 96 hammer_alist_template(&Buf_alist_config, HAMMER_FSBUF_MAXBLKS, 97 1, HAMMER_FSBUF_METAELMS); 98 hammer_alist_template(&Vol_normal_alist_config, HAMMER_VOL_MAXCLUSTERS, 99 1, HAMMER_VOL_METAELMS_1LYR); 100 hammer_alist_template(&Vol_super_alist_config, 101 HAMMER_VOL_MAXSUPERCLUSTERS, 102 HAMMER_SCL_MAXCLUSTERS, HAMMER_VOL_METAELMS_2LYR); 103 hammer_super_alist_template(&Vol_super_alist_config); 104 hammer_alist_template(&Supercl_alist_config, HAMMER_VOL_MAXCLUSTERS, 105 1, HAMMER_SUPERCL_METAELMS); 106 hammer_alist_template(&Clu_master_alist_config, HAMMER_CLU_MAXBUFFERS, 107 1, HAMMER_CLU_MASTER_METAELMS); 108 hammer_alist_template(&Clu_slave_alist_config, HAMMER_CLU_MAXBUFFERS, 109 HAMMER_FSBUF_MAXBLKS, HAMMER_CLU_SLAVE_METAELMS); 110 hammer_buffer_alist_template(&Clu_slave_alist_config); 111 112 /* 113 * Parse arguments 114 */ 115 while ((ch = getopt(ac, av, "L:c:S")) != -1) { 116 switch(ch) { 117 case 'L': 118 label = optarg; 119 break; 120 case 'c': 121 ClusterSize = getsize(optarg, 122 HAMMER_BUFSIZE * 256LL, 123 HAMMER_CLU_MAXBYTES, 1); 124 break; 125 case 'S': 126 /* 127 * Force the use of super-clusters 128 */ 129 UsingSuperClusters = 1; 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 exit(1); 141 } 142 143 /* 144 * Collect volume information 145 */ 146 ac -= optind; 147 av += optind; 148 NumVolumes = ac; 149 150 total = 0; 151 for (i = 0; i < NumVolumes; ++i) { 152 struct volume_info *vol; 153 154 vol = calloc(1, sizeof(struct volume_info)); 155 vol->fd = -1; 156 vol->vol_no = i; 157 vol->name = av[i]; 158 vol->next = VolBase; 159 VolBase = vol; 160 161 /* 162 * Load up information on the volume and initialize 163 * its remaining fields. 164 */ 165 check_volume(vol); 166 total += vol->size; 167 } 168 169 /* 170 * Calculate the size of a cluster. A cluster is broken 171 * down into 256 chunks which must be at least filesystem buffer 172 * sized. This gives us a minimum chunk size of around 4MB. 173 */ 174 if (ClusterSize == 0) { 175 ClusterSize = HAMMER_BUFSIZE * 256; 176 while (ClusterSize < total / NumVolumes / 256 && 177 ClusterSize < HAMMER_CLU_MAXBYTES) { 178 ClusterSize <<= 1; 179 } 180 } 181 182 printf("---------------------------------------------\n"); 183 printf("%d volume%s total size %s\n", 184 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total)); 185 printf("cluster-size: %s\n", sizetostr(ClusterSize)); 186 187 if (UsingSuperClusters) { 188 max_volume_size = (int64_t)HAMMER_VOL_MAXSUPERCLUSTERS * \ 189 HAMMER_SCL_MAXCLUSTERS * ClusterSize; 190 } else { 191 max_volume_size = (int64_t)HAMMER_VOL_MAXCLUSTERS * ClusterSize; 192 } 193 printf("max-volume-size: %s\n", sizetostr(max_volume_size)); 194 195 printf("max-filesystem-size: %s\n", 196 (max_volume_size * 32768LL < max_volume_size) ? 197 "Unlimited" : 198 sizetostr(max_volume_size * 32768LL)); 199 printf("\n"); 200 201 /* 202 * Format the volumes. 203 */ 204 for (i = 0; i < NumVolumes; ++i) { 205 format_volume(get_volume(i), NumVolumes, label); 206 } 207 flush_all_volumes(); 208 return(0); 209 } 210 211 static 212 void 213 usage(void) 214 { 215 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n"); 216 exit(1); 217 } 218 219 /* 220 * Convert the size in bytes to a human readable string. 221 */ 222 static const char * 223 sizetostr(off_t size) 224 { 225 static char buf[32]; 226 227 if (size < 1024 / 2) { 228 snprintf(buf, sizeof(buf), "%6.2f", (double)size); 229 } else if (size < 1024 * 1024 / 2) { 230 snprintf(buf, sizeof(buf), "%6.2fKB", 231 (double)size / 1024); 232 } else if (size < 1024 * 1024 * 1024LL / 2) { 233 snprintf(buf, sizeof(buf), "%6.2fMB", 234 (double)size / (1024 * 1024)); 235 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 236 snprintf(buf, sizeof(buf), "%6.2fGB", 237 (double)size / (1024 * 1024 * 1024LL)); 238 } else { 239 snprintf(buf, sizeof(buf), "%6.2fTB", 240 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 241 } 242 return(buf); 243 } 244 245 /* 246 * Convert a string to a 64 bit signed integer with various requirements. 247 */ 248 static int64_t 249 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 250 { 251 int64_t val; 252 char *ptr; 253 254 val = strtoll(str, &ptr, 0); 255 switch(*ptr) { 256 case 't': 257 case 'T': 258 val *= 1024; 259 /* fall through */ 260 case 'g': 261 case 'G': 262 val *= 1024; 263 /* fall through */ 264 case 'm': 265 case 'M': 266 val *= 1024; 267 /* fall through */ 268 case 'k': 269 case 'K': 270 val *= 1024; 271 break; 272 default: 273 errx(1, "Unknown suffix in number '%s'\n", str); 274 /* not reached */ 275 } 276 if (ptr[1]) { 277 errx(1, "Unknown suffix in number '%s'\n", str); 278 /* not reached */ 279 } 280 if (val < minval) { 281 errx(1, "Value too small: %s, min is %s\n", 282 str, sizetostr(minval)); 283 /* not reached */ 284 } 285 if (val > maxval) { 286 errx(1, "Value too large: %s, max is %s\n", 287 str, sizetostr(maxval)); 288 /* not reached */ 289 } 290 if (powerof2 && (val ^ (val - 1)) != ((val << 1) - 1)) { 291 errx(1, "Value not power of 2: %s\n", str); 292 /* not reached */ 293 } 294 return(val); 295 } 296 297 /* 298 * Generate a transaction id 299 */ 300 static hammer_tid_t 301 createtid(void) 302 { 303 static hammer_tid_t lasttid; 304 struct timeval tv; 305 306 if (lasttid == 0) { 307 gettimeofday(&tv, NULL); 308 lasttid = tv.tv_sec * 1000000000LL + 309 tv.tv_usec * 1000LL; 310 } 311 return(lasttid++); 312 } 313 314 /* 315 * Check basic volume characteristics. HAMMER filesystems use a minimum 316 * of a 16KB filesystem buffer size. 317 */ 318 static 319 void 320 check_volume(struct volume_info *vol) 321 { 322 struct partinfo pinfo; 323 struct stat st; 324 325 /* 326 * Get basic information about the volume 327 */ 328 vol->fd = open(vol->name, O_RDWR); 329 if (vol->fd < 0) 330 err(1, "Unable to open %s R+W", vol->name); 331 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) { 332 /* 333 * Allow the formatting of regular filews as HAMMER volumes 334 */ 335 if (fstat(vol->fd, &st) < 0) 336 err(1, "Unable to stat %s", vol->name); 337 vol->size = st.st_size; 338 vol->type = "REGFILE"; 339 } else { 340 /* 341 * When formatting a block device as a HAMMER volume the 342 * sector size must be compatible. HAMMER uses 16384 byte 343 * filesystem buffers. 344 */ 345 if (pinfo.reserved_blocks) { 346 errx(1, "HAMMER cannot be placed in a partition " 347 "which overlaps the disklabel or MBR"); 348 } 349 if (pinfo.media_blksize > 16384 || 350 16384 % pinfo.media_blksize) { 351 errx(1, "A media sector size of %d is not supported", 352 pinfo.media_blksize); 353 } 354 355 vol->size = pinfo.media_size; 356 vol->type = "DEVICE"; 357 } 358 printf("Volume %d %s %-15s size %s\n", 359 vol->vol_no, vol->type, vol->name, 360 sizetostr(vol->size)); 361 362 /* 363 * Strictly speaking we do not need to enable super clusters unless 364 * we have volumes > 2TB, but turning them on doesn't really hurt 365 * and if we don't the user may get confused if he tries to expand 366 * the size of an existing volume. 367 */ 368 if (vol->size > 200LL * 1024 * 1024 * 1024 && !UsingSuperClusters) { 369 UsingSuperClusters = 1; 370 printf("Enabling super-clusters\n"); 371 } 372 373 /* 374 * Reserve space for (future) boot junk 375 */ 376 vol->vol_cluster_off = HAMMER_BUFSIZE * 16; 377 } 378 379 /* 380 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0. 381 */ 382 static 383 void 384 format_volume(struct volume_info *vol, int nvols, const char *label) 385 { 386 struct hammer_volume_ondisk *ondisk; 387 int32_t nclusters; 388 int32_t minclsize; 389 int32_t nscl_groups; 390 int64_t scl_group_size; 391 int64_t scl_header_size; 392 int64_t n64; 393 394 /* 395 * The last cluster in a volume may wind up truncated. It must be 396 * at least minclsize to really be workable as a cluster. 397 */ 398 minclsize = ClusterSize / 4; 399 if (minclsize < HAMMER_BUFSIZE * 64) 400 minclsize = HAMMER_BUFSIZE * 64; 401 402 /* 403 * Initialize basic information in the on-disk volume structure. 404 */ 405 ondisk = vol->ondisk; 406 407 ondisk->vol_fsid = Hammer_FSId; 408 ondisk->vol_fstype = Hammer_FSType; 409 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label); 410 ondisk->vol_no = vol->vol_no; 411 ondisk->vol_count = nvols; 412 ondisk->vol_version = 1; 413 ondisk->vol_clsize = ClusterSize; 414 if (UsingSuperClusters) 415 ondisk->vol_flags = HAMMER_VOLF_USINGSUPERCL; 416 417 ondisk->vol_beg = vol->vol_cluster_off; 418 ondisk->vol_end = vol->size; 419 420 if (ondisk->vol_end < ondisk->vol_beg) { 421 errx(1, "volume %d %s is too small to hold the volume header", 422 vol->vol_no, vol->name); 423 } 424 425 /* 426 * Our A-lists have been initialized but are marked all-allocated. 427 * Calculate the actual number of clusters in the volume and free 428 * them to get the filesystem ready for work. The clusters will 429 * be initialized on-demand. 430 * 431 * If using super-clusters we must still calculate nclusters but 432 * we only need to initialize superclusters that are not going 433 * to wind up in the all-free state, which will only be the last 434 * supercluster. hammer_alist_free() will recurse into the 435 * supercluster infrastructure and create the necessary superclusters. 436 * 437 * NOTE: The nclusters calculation ensures that the volume EOF does 438 * not occur in the middle of a supercluster buffer array. 439 */ 440 if (UsingSuperClusters) { 441 /* 442 * Figure out how many full super-cluster groups we will have. 443 * This calculation does not include the partial supercluster 444 * group at the end. 445 */ 446 scl_header_size = (int64_t)HAMMER_BUFSIZE * 447 HAMMER_VOL_SUPERCLUSTER_GROUP; 448 scl_group_size = scl_header_size + 449 (int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP * 450 ClusterSize * HAMMER_SCL_MAXCLUSTERS; 451 nscl_groups = (ondisk->vol_end - ondisk->vol_beg) / 452 scl_group_size; 453 nclusters = nscl_groups * HAMMER_SCL_MAXCLUSTERS * 454 HAMMER_VOL_SUPERCLUSTER_GROUP; 455 456 /* 457 * Figure out how much space we have left and calculate the 458 * remaining number of clusters. 459 */ 460 n64 = (ondisk->vol_end - ondisk->vol_beg) - 461 (nscl_groups * scl_group_size); 462 if (n64 > scl_header_size) { 463 nclusters += (n64 + minclsize) / ClusterSize; 464 } 465 printf("%d clusters, %d full super-cluster groups\n", 466 nclusters, nscl_groups); 467 hammer_alist_free(&vol->clu_alist, 0, nclusters); 468 } else { 469 nclusters = (ondisk->vol_end - ondisk->vol_beg + minclsize) / 470 ClusterSize; 471 if (nclusters > HAMMER_VOL_MAXCLUSTERS) { 472 errx(1, "Volume is too large, max %s\n", 473 sizetostr((int64_t)nclusters * ClusterSize)); 474 } 475 hammer_alist_free(&vol->clu_alist, 0, nclusters); 476 } 477 ondisk->vol_nclusters = nclusters; 478 479 /* 480 * Place the root cluster in volume 0. 481 */ 482 ondisk->vol_rootvol = 0; 483 if (ondisk->vol_no == ondisk->vol_rootvol) { 484 ondisk->vol0_root_clu_id = format_cluster(vol, 1); 485 ondisk->vol0_recid = 1; 486 /* global next TID */ 487 ondisk->vol0_nexttid = createtid(); 488 } 489 } 490 491 /* 492 * Format a hammer cluster. Returns byte offset in volume of cluster. 493 */ 494 static 495 int32_t 496 format_cluster(struct volume_info *vol, int isroot) 497 { 498 hammer_tid_t clu_id = createtid(); 499 struct cluster_info *cluster; 500 struct hammer_cluster_ondisk *ondisk; 501 int nbuffers; 502 int clno; 503 504 /* 505 * Allocate a cluster 506 */ 507 clno = hammer_alist_alloc(&vol->clu_alist, 1); 508 if (clno == HAMMER_ALIST_BLOCK_NONE) { 509 fprintf(stderr, "volume %d %s has insufficient space\n", 510 vol->vol_no, vol->name); 511 exit(1); 512 } 513 cluster = get_cluster(vol, clno); 514 printf("allocate cluster id=%016llx %d@%08llx\n", 515 clu_id, clno, cluster->clu_offset); 516 517 ondisk = cluster->ondisk; 518 519 ondisk->vol_fsid = vol->ondisk->vol_fsid; 520 ondisk->vol_fstype = vol->ondisk->vol_fstype; 521 ondisk->clu_gen = 1; 522 ondisk->clu_id = clu_id; 523 ondisk->clu_no = clno; 524 ondisk->clu_flags = 0; 525 ondisk->clu_start = HAMMER_BUFSIZE; 526 if (vol->size - cluster->clu_offset > ClusterSize) 527 ondisk->clu_limit = ClusterSize; 528 else 529 ondisk->clu_limit = (u_int32_t)(vol->size - cluster->clu_offset); 530 531 /* 532 * In-band filesystem buffer management A-List. The first filesystem 533 * buffer is the cluster header itself. 534 */ 535 nbuffers = ondisk->clu_limit / HAMMER_BUFSIZE; 536 hammer_alist_free(&cluster->alist_master, 1, nbuffers - 1); 537 printf("cluster %d has %d buffers\n", cluster->clu_no, nbuffers); 538 539 /* 540 * Buffer Iterators in elements. Each buffer has 256 elements. 541 * The data and B-Tree indices are forward allocations while the 542 * record index allocates backwards. 543 */ 544 ondisk->idx_data = 1 * HAMMER_FSBUF_MAXBLKS; 545 ondisk->idx_index = 0 * HAMMER_FSBUF_MAXBLKS; 546 ondisk->idx_record = nbuffers * HAMMER_FSBUF_MAXBLKS; 547 548 /* 549 * Initialize root cluster's parent cluster info. -1's 550 * indicate we are the root cluster and no parent exists. 551 */ 552 ondisk->clu_btree_parent_vol_no = -1; 553 ondisk->clu_btree_parent_clu_no = -1; 554 ondisk->clu_btree_parent_offset = -1; 555 ondisk->clu_btree_parent_clu_gen = -1; 556 557 /* 558 * Cluster 0 is the root cluster. Set the B-Tree range for this 559 * cluster to the entire key space and format the root directory. 560 * 561 * Note that delete_tid for the ending range must be set to 0, 562 * 0 indicates 'not deleted', aka 'the most recent'. See 563 * hammer_btree_cmp() in sys/vfs/hammer/hammer_btree.c. 564 * 565 * The root cluster's key space represents the entire key space for 566 * the filesystem. The btree_end element appears to be inclusive 567 * only because we can't overflow our variables. It's actually 568 * non-inclusive... that is, it is a right-side boundary element. 569 */ 570 if (isroot) { 571 ondisk->clu_btree_beg.obj_id = -0x8000000000000000LL; 572 ondisk->clu_btree_beg.key = -0x8000000000000000LL; 573 ondisk->clu_btree_beg.create_tid = 0; 574 ondisk->clu_btree_beg.delete_tid = 0; 575 ondisk->clu_btree_beg.rec_type = 0; 576 ondisk->clu_btree_beg.obj_type = 0; 577 578 ondisk->clu_btree_end.obj_id = 0x7FFFFFFFFFFFFFFFLL; 579 ondisk->clu_btree_end.key = 0x7FFFFFFFFFFFFFFFLL; 580 ondisk->clu_btree_end.create_tid = 0xFFFFFFFFFFFFFFFFULL; 581 ondisk->clu_btree_end.delete_tid = 0; /* special case */ 582 ondisk->clu_btree_end.rec_type = 0xFFFFU; 583 ondisk->clu_btree_end.obj_type = 0; 584 585 format_root(cluster); 586 } 587 588 /* 589 * Write-out and update the index, record, and cluster buffers 590 */ 591 return(clno); 592 } 593 594 /* 595 * Format the root directory. 596 */ 597 static 598 void 599 format_root(struct cluster_info *cluster) 600 { 601 int32_t btree_off; 602 int32_t rec_off; 603 int32_t data_off; 604 hammer_node_ondisk_t bnode; 605 union hammer_record_ondisk *rec; 606 struct hammer_inode_data *idata; 607 hammer_btree_elm_t elm; 608 609 bnode = alloc_btree_element(cluster, &btree_off); 610 rec = alloc_record_element(cluster, &rec_off); 611 idata = alloc_data_element(cluster, sizeof(*idata), &data_off); 612 613 /* 614 * Populate the inode data and inode record for the root directory. 615 */ 616 idata->version = HAMMER_INODE_DATA_VERSION; 617 idata->mode = 0755; 618 619 rec->base.base.obj_id = 1; 620 rec->base.base.key = 0; 621 rec->base.base.create_tid = createtid(); 622 rec->base.base.delete_tid = 0; 623 rec->base.base.rec_type = HAMMER_RECTYPE_INODE; 624 rec->base.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 625 rec->base.data_offset = data_off; 626 rec->base.data_len = sizeof(*idata); 627 rec->base.data_crc = crc32(idata, sizeof(*idata)); 628 rec->inode.ino_atime = rec->base.base.create_tid; 629 rec->inode.ino_mtime = rec->base.base.create_tid; 630 rec->inode.ino_size = 0; 631 rec->inode.ino_nlinks = 1; 632 633 /* 634 * Assign the cluster's root B-Tree node. 635 */ 636 assert(cluster->ondisk->clu_btree_root == 0); 637 cluster->ondisk->clu_btree_root = btree_off; 638 639 /* 640 * Create the root of the B-Tree. The root is a leaf node so we 641 * do not have to worry about boundary elements. 642 */ 643 bnode->count = 1; 644 bnode->type = HAMMER_BTREE_TYPE_LEAF; 645 646 elm = &bnode->elms[0]; 647 elm->base = rec->base.base; 648 elm->leaf.rec_offset = rec_off; 649 elm->leaf.data_offset = rec->base.data_offset; 650 elm->leaf.data_len = rec->base.data_len; 651 elm->leaf.data_crc = rec->base.data_crc; 652 } 653 654 void 655 panic(const char *ctl, ...) 656 { 657 va_list va; 658 659 va_start(va, ctl); 660 vfprintf(stderr, ctl, va); 661 va_end(va); 662 fprintf(stderr, "\n"); 663 exit(1); 664 } 665 666