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