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