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.44 2008/08/21 23:32:27 thomas 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 off_t total_size); 44 static hammer_off_t format_root(const char *label); 45 static u_int64_t nowtime(void); 46 static void usage(void); 47 48 static int ForceOpt = 0; 49 static int HammerVersion = -1; 50 51 #define GIG (1024LL*1024*1024) 52 53 int 54 main(int ac, char **av) 55 { 56 u_int32_t status; 57 off_t total; 58 int ch; 59 int i; 60 const char *label = NULL; 61 struct volume_info *vol; 62 char *fsidstr; 63 64 /* 65 * Sanity check basic filesystem structures. No cookies for us 66 * if it gets broken! 67 */ 68 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE); 69 assert(sizeof(struct hammer_blockmap_layer1) == 32); 70 assert(sizeof(struct hammer_blockmap_layer2) == 16); 71 72 /* 73 * Generate a filesystem id and lookup the filesystem type 74 */ 75 uuidgen(&Hammer_FSId, 1); 76 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status); 77 if (status != uuid_s_ok) { 78 errx(1, "uuids file does not have the DragonFly " 79 "HAMMER filesystem type"); 80 } 81 82 /* 83 * Parse arguments 84 */ 85 while ((ch = getopt(ac, av, "fL:b:m:u:V:")) != -1) { 86 switch(ch) { 87 case 'f': 88 ForceOpt = 1; 89 break; 90 case 'L': 91 label = optarg; 92 break; 93 case 'b': 94 BootAreaSize = getsize(optarg, 95 HAMMER_BUFSIZE, 96 HAMMER_BOOT_MAXBYTES, 2); 97 break; 98 case 'm': 99 MemAreaSize = getsize(optarg, 100 HAMMER_BUFSIZE, 101 HAMMER_MEM_MAXBYTES, 2); 102 break; 103 case 'u': 104 UndoBufferSize = getsize(optarg, 105 HAMMER_LARGEBLOCK_SIZE, 106 HAMMER_LARGEBLOCK_SIZE * 107 HAMMER_UNDO_LAYER2, 2); 108 if (UndoBufferSize < 100*1024*1024 && ForceOpt == 0) 109 errx(1, "The minimum UNDO fifo size is 100M\n"); 110 if (UndoBufferSize < 100*1024*1024) { 111 fprintf(stderr, 112 "WARNING: you have specified an UNDO " 113 "FIFO size less than 100M, which may\n" 114 "lead to VFS panics.\n"); 115 } 116 break; 117 case 'V': 118 HammerVersion = strtol(optarg, NULL, 0); 119 if (HammerVersion < HAMMER_VOL_VERSION_MIN || 120 HammerVersion >= HAMMER_VOL_VERSION_WIP) { 121 errx(1, 122 "I don't understand how to format " 123 "HAMMER version %d\n", 124 HammerVersion); 125 } 126 break; 127 default: 128 usage(); 129 break; 130 } 131 } 132 133 if (label == NULL) { 134 fprintf(stderr, 135 "newfs_hammer: A filesystem label must be specified\n"); 136 exit(1); 137 } 138 139 if (HammerVersion < 0) { 140 size_t olen = sizeof(HammerVersion); 141 HammerVersion = HAMMER_VOL_VERSION_DEFAULT; 142 if (sysctlbyname("vfs.hammer.supported_version", 143 &HammerVersion, &olen, NULL, 0) == 0) { 144 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) { 145 HammerVersion = HAMMER_VOL_VERSION_WIP - 1; 146 fprintf(stderr, 147 "newfs_hammer: WARNING: HAMMER VFS " 148 "supports higher version then I " 149 "understand,\n" 150 "using version %d\n", 151 HammerVersion); 152 } 153 } else { 154 fprintf(stderr, 155 "newfs_hammer: WARNING: HAMMER VFS not " 156 "loaded, cannot get version info.\n" 157 "Using version %d\n", 158 HAMMER_VOL_VERSION_DEFAULT); 159 } 160 } 161 162 /* 163 * Collect volume information 164 */ 165 ac -= optind; 166 av += optind; 167 NumVolumes = ac; 168 RootVolNo = 0; 169 170 if (NumVolumes == 0) { 171 fprintf(stderr, 172 "newfs_hammer: You must specify at least one special file (volume)\n"); 173 exit(1); 174 } 175 176 if (NumVolumes > HAMMER_MAX_VOLUMES) { 177 fprintf(stderr, 178 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES); 179 exit(1); 180 } 181 182 total = 0; 183 for (i = 0; i < NumVolumes; ++i) { 184 vol = setup_volume(i, av[i], 1, O_RDWR); 185 186 /* 187 * Load up information on the volume and initialize 188 * its remaining fields. 189 */ 190 check_volume(vol); 191 total += vol->size; 192 } 193 194 /* 195 * Calculate defaults for the boot and memory area sizes. 196 */ 197 if (BootAreaSize == 0) { 198 BootAreaSize = HAMMER_BOOT_NOMBYTES; 199 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES) 200 BootAreaSize >>= 1; 201 if (BootAreaSize < HAMMER_BOOT_MINBYTES) 202 BootAreaSize = 0; 203 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) { 204 BootAreaSize = HAMMER_BOOT_MINBYTES; 205 } 206 if (MemAreaSize == 0) { 207 MemAreaSize = HAMMER_MEM_NOMBYTES; 208 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES) 209 MemAreaSize >>= 1; 210 if (MemAreaSize < HAMMER_MEM_MINBYTES) 211 MemAreaSize = 0; 212 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) { 213 MemAreaSize = HAMMER_MEM_MINBYTES; 214 } 215 216 /* 217 * Format the volumes. Format the root volume first so we can 218 * bootstrap the freemap. 219 */ 220 format_volume(get_volume(RootVolNo), NumVolumes, label, total); 221 for (i = 0; i < NumVolumes; ++i) { 222 if (i != RootVolNo) 223 format_volume(get_volume(i), NumVolumes, label, total); 224 } 225 226 /* 227 * Pre-size the blockmap layer1/layer2 infrastructure to the zone 228 * limit. If we do this the filesystem does not have to allocate 229 * new layer2 blocks which reduces the chances of the reblocker 230 * having to fallback to an extremely inefficient algorithm. 231 */ 232 vol = get_volume(RootVolNo); 233 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks; 234 vol->cache.modified = 1; 235 uuid_to_string(&Hammer_FSId, &fsidstr, &status); 236 237 printf("---------------------------------------------\n"); 238 printf("%d volume%s total size %s version %d\n", 239 NumVolumes, (NumVolumes == 1 ? "" : "s"), 240 sizetostr(total), HammerVersion); 241 printf("boot-area-size: %s\n", sizetostr(BootAreaSize)); 242 printf("memory-log-size: %s\n", sizetostr(MemAreaSize)); 243 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize)); 244 printf("total-pre-allocated: %s\n", 245 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK)); 246 printf("fsid: %s\n", fsidstr); 247 printf("\n"); 248 printf("NOTE: Please remember that you may have to manually set up a\n" 249 "cron job to prune and reblock the filesystem regularly.\n" 250 "By default, the system automatically runs 'hammer cleanup'\n" 251 "on a nightly basis. The periodic.conf(5) variable\n" 252 "'daily_clean_hammer_enable' can be unset to disable this.\n" 253 "Also see 'man hammer' and 'man HAMMER' for more information.\n"); 254 if (total < 50*GIG) { 255 printf("\nWARNING: HAMMER filesystems less than 50G are " 256 "not recommended!\n" 257 "You may have to run 'hammer prune-everything' and " 258 "'hammer reblock'\n" 259 "quite often, even if using a nohistory mount.\n"); 260 } 261 flush_all_volumes(); 262 return(0); 263 } 264 265 static 266 void 267 usage(void) 268 { 269 fprintf(stderr, 270 "newfs_hammer -L label [-b bootsize] [-m savesize] [-u undosize] " 271 "special ...\n" 272 ); 273 exit(1); 274 } 275 276 /* 277 * Convert the size in bytes to a human readable string. 278 */ 279 static 280 const char * 281 sizetostr(off_t size) 282 { 283 static char buf[32]; 284 285 if (size < 1024 / 2) { 286 snprintf(buf, sizeof(buf), "%6.2f", (double)size); 287 } else if (size < 1024 * 1024 / 2) { 288 snprintf(buf, sizeof(buf), "%6.2fKB", 289 (double)size / 1024); 290 } else if (size < 1024 * 1024 * 1024LL / 2) { 291 snprintf(buf, sizeof(buf), "%6.2fMB", 292 (double)size / (1024 * 1024)); 293 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 294 snprintf(buf, sizeof(buf), "%6.2fGB", 295 (double)size / (1024 * 1024 * 1024LL)); 296 } else { 297 snprintf(buf, sizeof(buf), "%6.2fTB", 298 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 299 } 300 return(buf); 301 } 302 303 /* 304 * Convert a string to a 64 bit signed integer with various requirements. 305 */ 306 static int64_t 307 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 308 { 309 int64_t val; 310 char *ptr; 311 312 val = strtoll(str, &ptr, 0); 313 switch(*ptr) { 314 case 't': 315 case 'T': 316 val *= 1024; 317 /* fall through */ 318 case 'g': 319 case 'G': 320 val *= 1024; 321 /* fall through */ 322 case 'm': 323 case 'M': 324 val *= 1024; 325 /* fall through */ 326 case 'k': 327 case 'K': 328 val *= 1024; 329 break; 330 default: 331 errx(1, "Unknown suffix in number '%s'\n", str); 332 /* not reached */ 333 } 334 if (ptr[1]) { 335 errx(1, "Unknown suffix in number '%s'\n", str); 336 /* not reached */ 337 } 338 if (val < minval) { 339 errx(1, "Value too small: %s, min is %s\n", 340 str, sizetostr(minval)); 341 /* not reached */ 342 } 343 if (val > maxval) { 344 errx(1, "Value too large: %s, max is %s\n", 345 str, sizetostr(maxval)); 346 /* not reached */ 347 } 348 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 349 errx(1, "Value not power of 2: %s\n", str); 350 /* not reached */ 351 } 352 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 353 errx(1, "Value not an integral multiple of %dK: %s", 354 HAMMER_BUFSIZE / 1024, str); 355 /* not reached */ 356 } 357 return(val); 358 } 359 360 /* 361 * Generate a transaction id. Transaction ids are no longer time-based. 362 * Put the nail in the coffin by not making the first one time-based. 363 * 364 * We could start at 1 here but start at 2^32 to reserve a small domain for 365 * possible future use. 366 */ 367 static hammer_tid_t 368 createtid(void) 369 { 370 static hammer_tid_t lasttid; 371 372 if (lasttid == 0) 373 lasttid = 0x0000000100000000ULL; 374 return(lasttid++); 375 } 376 377 static u_int64_t 378 nowtime(void) 379 { 380 struct timeval tv; 381 u_int64_t xtime; 382 383 gettimeofday(&tv, NULL); 384 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 385 return(xtime); 386 } 387 388 /* 389 * Check basic volume characteristics. HAMMER filesystems use a minimum 390 * of a 16KB filesystem buffer size. 391 */ 392 static 393 void 394 check_volume(struct volume_info *vol) 395 { 396 struct partinfo pinfo; 397 struct stat st; 398 399 /* 400 * Get basic information about the volume 401 */ 402 vol->fd = open(vol->name, O_RDWR); 403 if (vol->fd < 0) 404 err(1, "Unable to open %s R+W", vol->name); 405 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) { 406 /* 407 * Allow the formatting of regular files as HAMMER volumes 408 */ 409 if (fstat(vol->fd, &st) < 0) 410 err(1, "Unable to stat %s", vol->name); 411 vol->size = st.st_size; 412 vol->type = "REGFILE"; 413 } else { 414 /* 415 * When formatting a block device as a HAMMER volume the 416 * sector size must be compatible. HAMMER uses 16384 byte 417 * filesystem buffers. 418 */ 419 if (pinfo.reserved_blocks) { 420 errx(1, "HAMMER cannot be placed in a partition " 421 "which overlaps the disklabel or MBR"); 422 } 423 if (pinfo.media_blksize > 16384 || 424 16384 % pinfo.media_blksize) { 425 errx(1, "A media sector size of %d is not supported", 426 pinfo.media_blksize); 427 } 428 429 vol->size = pinfo.media_size; 430 vol->type = "DEVICE"; 431 } 432 printf("Volume %d %s %-15s size %s\n", 433 vol->vol_no, vol->type, vol->name, 434 sizetostr(vol->size)); 435 436 /* 437 * Reserve space for (future) header junk, setup our poor-man's 438 * bigblock allocator. 439 */ 440 vol->vol_alloc = HAMMER_BUFSIZE * 16; 441 } 442 443 /* 444 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0. 445 */ 446 static 447 void 448 format_volume(struct volume_info *vol, int nvols, const char *label, 449 off_t total_size __unused) 450 { 451 struct volume_info *root_vol; 452 struct hammer_volume_ondisk *ondisk; 453 int64_t freeblks; 454 int64_t freebytes; 455 int i; 456 457 /* 458 * Initialize basic information in the on-disk volume structure. 459 */ 460 ondisk = vol->ondisk; 461 462 ondisk->vol_fsid = Hammer_FSId; 463 ondisk->vol_fstype = Hammer_FSType; 464 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label); 465 ondisk->vol_no = vol->vol_no; 466 ondisk->vol_count = nvols; 467 ondisk->vol_version = HammerVersion; 468 469 ondisk->vol_bot_beg = vol->vol_alloc; 470 vol->vol_alloc += BootAreaSize; 471 ondisk->vol_mem_beg = vol->vol_alloc; 472 vol->vol_alloc += MemAreaSize; 473 474 /* 475 * The remaining area is the zone 2 buffer allocation area. These 476 * buffers 477 */ 478 ondisk->vol_buf_beg = vol->vol_alloc; 479 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 480 481 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) { 482 errx(1, "volume %d %s is too small to hold the volume header", 483 vol->vol_no, vol->name); 484 } 485 486 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 487 HAMMER_BUFSIZE; 488 ondisk->vol_blocksize = HAMMER_BUFSIZE; 489 490 ondisk->vol_rootvol = RootVolNo; 491 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 492 493 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 494 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64); 495 496 /* 497 * Format the root volume. 498 */ 499 if (vol->vol_no == RootVolNo) { 500 /* 501 * Starting TID 502 */ 503 ondisk->vol0_next_tid = createtid(); 504 505 format_freemap(vol, 506 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]); 507 508 freeblks = initialize_freemap(vol); 509 ondisk->vol0_stat_freebigblocks = freeblks; 510 511 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64; 512 if (freebytes < 1*GIG && ForceOpt == 0) { 513 errx(1, "Cannot create a HAMMER filesystem less than " 514 "1GB unless you use -f. HAMMER filesystems\n" 515 "less than 50G are not recommended\n"); 516 } 517 518 for (i = 8; i < HAMMER_MAX_ZONES; ++i) { 519 format_blockmap(&ondisk->vol0_blockmap[i], 520 HAMMER_ZONE_ENCODE(i, 0)); 521 } 522 format_undomap(ondisk); 523 524 ondisk->vol0_btree_root = format_root(label); 525 ++ondisk->vol0_stat_inodes; /* root inode */ 526 } else { 527 freeblks = initialize_freemap(vol); 528 root_vol = get_volume(RootVolNo); 529 root_vol->cache.modified = 1; 530 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 531 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 532 rel_volume(root_vol); 533 } 534 } 535 536 /* 537 * Format the root directory. 538 */ 539 static 540 hammer_off_t 541 format_root(const char *label) 542 { 543 hammer_off_t btree_off; 544 hammer_off_t pfsd_off; 545 hammer_off_t data_off; 546 hammer_tid_t create_tid; 547 hammer_node_ondisk_t bnode; 548 struct hammer_inode_data *idata; 549 hammer_pseudofs_data_t pfsd; 550 struct buffer_info *data_buffer1 = NULL; 551 struct buffer_info *data_buffer2 = NULL; 552 hammer_btree_elm_t elm; 553 u_int64_t xtime; 554 555 bnode = alloc_btree_element(&btree_off); 556 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1); 557 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 558 create_tid = createtid(); 559 xtime = nowtime(); 560 561 /* 562 * Populate the inode data and inode record for the root directory. 563 */ 564 idata->version = HAMMER_INODE_DATA_VERSION; 565 idata->mode = 0755; 566 idata->ctime = xtime; 567 idata->mtime = xtime; 568 idata->atime = xtime; 569 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 570 idata->size = 0; 571 idata->nlinks = 1; 572 if (HammerVersion >= HAMMER_VOL_VERSION_TWO) 573 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO; 574 575 pfsd->sync_low_tid = 1; 576 pfsd->sync_beg_tid = 0; 577 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 578 pfsd->shared_uuid = Hammer_FSId; 579 pfsd->unique_uuid = Hammer_FSId; 580 pfsd->reserved01 = 0; 581 pfsd->mirror_flags = 0; 582 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 583 584 /* 585 * Create the root of the B-Tree. The root is a leaf node so we 586 * do not have to worry about boundary elements. 587 */ 588 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD; 589 bnode->count = 2; 590 bnode->type = HAMMER_BTREE_TYPE_LEAF; 591 592 elm = &bnode->elms[0]; 593 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 594 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE; 595 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 596 elm->leaf.base.key = 0; 597 elm->leaf.base.create_tid = create_tid; 598 elm->leaf.base.delete_tid = 0; 599 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 600 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 601 elm->leaf.create_ts = (u_int32_t)time(NULL); 602 603 elm->leaf.data_offset = data_off; 604 elm->leaf.data_len = sizeof(*idata); 605 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 606 607 elm = &bnode->elms[1]; 608 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 609 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC; 610 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 611 elm->leaf.base.key = 0; 612 elm->leaf.base.create_tid = create_tid; 613 elm->leaf.base.delete_tid = 0; 614 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 615 elm->leaf.base.obj_type = 0; 616 elm->leaf.create_ts = (u_int32_t)time(NULL); 617 618 elm->leaf.data_offset = pfsd_off; 619 elm->leaf.data_len = sizeof(*pfsd); 620 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 621 622 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 623 624 return(btree_off); 625 } 626 627