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 to set up a cron job to prune and\n" 208 "reblock the filesystem regularly, see 'man hammer' for\n" 209 "more information.\n"); 210 if (total < 50*GIG) { 211 printf("\nWARNING: HAMMER filesystems less than 50G are " 212 "not recommended!\n" 213 "You may have to run 'hammer prune-everything' and " 214 "'hammer reblock'\n" 215 "quite often, even if using a nohistory mount.\n"); 216 } 217 flush_all_volumes(); 218 return(0); 219 } 220 221 static 222 void 223 usage(void) 224 { 225 fprintf(stderr, 226 "newfs_hammer -L label [-b bootsize] [-m savesize] [-u undosize] " 227 "special ...\n" 228 ); 229 exit(1); 230 } 231 232 /* 233 * Convert the size in bytes to a human readable string. 234 */ 235 static 236 const char * 237 sizetostr(off_t size) 238 { 239 static char buf[32]; 240 241 if (size < 1024 / 2) { 242 snprintf(buf, sizeof(buf), "%6.2f", (double)size); 243 } else if (size < 1024 * 1024 / 2) { 244 snprintf(buf, sizeof(buf), "%6.2fKB", 245 (double)size / 1024); 246 } else if (size < 1024 * 1024 * 1024LL / 2) { 247 snprintf(buf, sizeof(buf), "%6.2fMB", 248 (double)size / (1024 * 1024)); 249 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 250 snprintf(buf, sizeof(buf), "%6.2fGB", 251 (double)size / (1024 * 1024 * 1024LL)); 252 } else { 253 snprintf(buf, sizeof(buf), "%6.2fTB", 254 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 255 } 256 return(buf); 257 } 258 259 /* 260 * Convert a string to a 64 bit signed integer with various requirements. 261 */ 262 static int64_t 263 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 264 { 265 int64_t val; 266 char *ptr; 267 268 val = strtoll(str, &ptr, 0); 269 switch(*ptr) { 270 case 't': 271 case 'T': 272 val *= 1024; 273 /* fall through */ 274 case 'g': 275 case 'G': 276 val *= 1024; 277 /* fall through */ 278 case 'm': 279 case 'M': 280 val *= 1024; 281 /* fall through */ 282 case 'k': 283 case 'K': 284 val *= 1024; 285 break; 286 default: 287 errx(1, "Unknown suffix in number '%s'\n", str); 288 /* not reached */ 289 } 290 if (ptr[1]) { 291 errx(1, "Unknown suffix in number '%s'\n", str); 292 /* not reached */ 293 } 294 if (val < minval) { 295 errx(1, "Value too small: %s, min is %s\n", 296 str, sizetostr(minval)); 297 /* not reached */ 298 } 299 if (val > maxval) { 300 errx(1, "Value too large: %s, max is %s\n", 301 str, sizetostr(maxval)); 302 /* not reached */ 303 } 304 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 305 errx(1, "Value not power of 2: %s\n", str); 306 /* not reached */ 307 } 308 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) { 309 errx(1, "Value not an integral multiple of %dK: %s", 310 HAMMER_BUFSIZE / 1024, str); 311 /* not reached */ 312 } 313 return(val); 314 } 315 316 /* 317 * Generate a transaction id. Transaction ids are no longer time-based. 318 * Put the nail in the coffin by not making the first one time-based. 319 * 320 * We could start at 1 here but start at 2^32 to reserve a small domain for 321 * possible future use. 322 */ 323 static hammer_tid_t 324 createtid(void) 325 { 326 static hammer_tid_t lasttid; 327 328 if (lasttid == 0) 329 lasttid = 0x0000000100000000ULL; 330 return(lasttid++); 331 } 332 333 static u_int64_t 334 nowtime(void) 335 { 336 struct timeval tv; 337 u_int64_t xtime; 338 339 gettimeofday(&tv, NULL); 340 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 341 return(xtime); 342 } 343 344 /* 345 * Check basic volume characteristics. HAMMER filesystems use a minimum 346 * of a 16KB filesystem buffer size. 347 */ 348 static 349 void 350 check_volume(struct volume_info *vol) 351 { 352 struct partinfo pinfo; 353 struct stat st; 354 355 /* 356 * Get basic information about the volume 357 */ 358 vol->fd = open(vol->name, O_RDWR); 359 if (vol->fd < 0) 360 err(1, "Unable to open %s R+W", vol->name); 361 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) { 362 /* 363 * Allow the formatting of regular files as HAMMER volumes 364 */ 365 if (fstat(vol->fd, &st) < 0) 366 err(1, "Unable to stat %s", vol->name); 367 vol->size = st.st_size; 368 vol->type = "REGFILE"; 369 } else { 370 /* 371 * When formatting a block device as a HAMMER volume the 372 * sector size must be compatible. HAMMER uses 16384 byte 373 * filesystem buffers. 374 */ 375 if (pinfo.reserved_blocks) { 376 errx(1, "HAMMER cannot be placed in a partition " 377 "which overlaps the disklabel or MBR"); 378 } 379 if (pinfo.media_blksize > 16384 || 380 16384 % pinfo.media_blksize) { 381 errx(1, "A media sector size of %d is not supported", 382 pinfo.media_blksize); 383 } 384 385 vol->size = pinfo.media_size; 386 vol->type = "DEVICE"; 387 } 388 printf("Volume %d %s %-15s size %s\n", 389 vol->vol_no, vol->type, vol->name, 390 sizetostr(vol->size)); 391 392 /* 393 * Reserve space for (future) header junk, setup our poor-man's 394 * bigblock allocator. 395 */ 396 vol->vol_alloc = HAMMER_BUFSIZE * 16; 397 } 398 399 /* 400 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0. 401 */ 402 static 403 void 404 format_volume(struct volume_info *vol, int nvols, const char *label, 405 off_t total_size __unused) 406 { 407 struct volume_info *root_vol; 408 struct hammer_volume_ondisk *ondisk; 409 int64_t freeblks; 410 int64_t freebytes; 411 int i; 412 413 /* 414 * Initialize basic information in the on-disk volume structure. 415 */ 416 ondisk = vol->ondisk; 417 418 ondisk->vol_fsid = Hammer_FSId; 419 ondisk->vol_fstype = Hammer_FSType; 420 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label); 421 ondisk->vol_no = vol->vol_no; 422 ondisk->vol_count = nvols; 423 ondisk->vol_version = HAMMER_VOL_VERSION_DEFAULT; 424 425 ondisk->vol_bot_beg = vol->vol_alloc; 426 vol->vol_alloc += BootAreaSize; 427 ondisk->vol_mem_beg = vol->vol_alloc; 428 vol->vol_alloc += MemAreaSize; 429 430 /* 431 * The remaining area is the zone 2 buffer allocation area. These 432 * buffers 433 */ 434 ondisk->vol_buf_beg = vol->vol_alloc; 435 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK; 436 437 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) { 438 errx(1, "volume %d %s is too small to hold the volume header", 439 vol->vol_no, vol->name); 440 } 441 442 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) / 443 HAMMER_BUFSIZE; 444 ondisk->vol_blocksize = HAMMER_BUFSIZE; 445 446 ondisk->vol_rootvol = RootVolNo; 447 ondisk->vol_signature = HAMMER_FSBUF_VOLUME; 448 449 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0); 450 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64); 451 452 /* 453 * Format the root volume. 454 */ 455 if (vol->vol_no == RootVolNo) { 456 /* 457 * Starting TID 458 */ 459 ondisk->vol0_next_tid = createtid(); 460 461 format_freemap(vol, 462 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]); 463 464 freeblks = initialize_freemap(vol); 465 ondisk->vol0_stat_freebigblocks = freeblks; 466 467 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64; 468 if (freebytes < 1*GIG && ForceOpt == 0) { 469 errx(1, "Cannot create a HAMMER filesystem less than " 470 "1GB unless you use -f. HAMMER filesystems\n" 471 "less than 50G are not recommended\n"); 472 } 473 474 for (i = 8; i < HAMMER_MAX_ZONES; ++i) { 475 format_blockmap(&ondisk->vol0_blockmap[i], 476 HAMMER_ZONE_ENCODE(i, 0)); 477 } 478 format_undomap(ondisk); 479 480 ondisk->vol0_btree_root = format_root(label); 481 ++ondisk->vol0_stat_inodes; /* root inode */ 482 } else { 483 freeblks = initialize_freemap(vol); 484 root_vol = get_volume(RootVolNo); 485 root_vol->cache.modified = 1; 486 root_vol->ondisk->vol0_stat_freebigblocks += freeblks; 487 root_vol->ondisk->vol0_stat_bigblocks += freeblks; 488 rel_volume(root_vol); 489 } 490 } 491 492 /* 493 * Format the root directory. 494 */ 495 static 496 hammer_off_t 497 format_root(const char *label) 498 { 499 hammer_off_t btree_off; 500 hammer_off_t pfsd_off; 501 hammer_off_t data_off; 502 hammer_tid_t create_tid; 503 hammer_node_ondisk_t bnode; 504 struct hammer_inode_data *idata; 505 hammer_pseudofs_data_t pfsd; 506 struct buffer_info *data_buffer1 = NULL; 507 struct buffer_info *data_buffer2 = NULL; 508 hammer_btree_elm_t elm; 509 u_int64_t xtime; 510 511 bnode = alloc_btree_element(&btree_off); 512 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1); 513 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2); 514 create_tid = createtid(); 515 xtime = nowtime(); 516 517 /* 518 * Populate the inode data and inode record for the root directory. 519 */ 520 idata->version = HAMMER_INODE_DATA_VERSION; 521 idata->mode = 0755; 522 idata->ctime = xtime; 523 idata->mtime = xtime; 524 idata->atime = xtime; 525 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY; 526 idata->size = 0; 527 idata->nlinks = 1; 528 529 pfsd->sync_low_tid = 1; 530 pfsd->sync_beg_tid = 0; 531 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */ 532 pfsd->shared_uuid = Hammer_FSId; 533 pfsd->unique_uuid = Hammer_FSId; 534 pfsd->reserved01 = 0; 535 pfsd->mirror_flags = 0; 536 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label); 537 538 /* 539 * Create the root of the B-Tree. The root is a leaf node so we 540 * do not have to worry about boundary elements. 541 */ 542 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD; 543 bnode->count = 2; 544 bnode->type = HAMMER_BTREE_TYPE_LEAF; 545 546 elm = &bnode->elms[0]; 547 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 548 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE; 549 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 550 elm->leaf.base.key = 0; 551 elm->leaf.base.create_tid = create_tid; 552 elm->leaf.base.delete_tid = 0; 553 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE; 554 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY; 555 elm->leaf.create_ts = (u_int32_t)time(NULL); 556 557 elm->leaf.data_offset = data_off; 558 elm->leaf.data_len = sizeof(*idata); 559 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE); 560 561 elm = &bnode->elms[1]; 562 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 563 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC; 564 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT; 565 elm->leaf.base.key = 0; 566 elm->leaf.base.create_tid = create_tid; 567 elm->leaf.base.delete_tid = 0; 568 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS; 569 elm->leaf.base.obj_type = 0; 570 elm->leaf.create_ts = (u_int32_t)time(NULL); 571 572 elm->leaf.data_offset = pfsd_off; 573 elm->leaf.data_len = sizeof(*pfsd); 574 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd)); 575 576 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE); 577 578 return(btree_off); 579 } 580 581