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