1 /* 2 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org> 3 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved. 4 * 5 * This code is derived from software contributed to The DragonFly Project 6 * by Matthew Dillon <dillon@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/time.h> 38 #include <sys/sysctl.h> 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <stddef.h> 43 #include <stdint.h> 44 #include <unistd.h> 45 #include <string.h> 46 #include <fcntl.h> 47 #include <assert.h> 48 #include <err.h> 49 #include <uuid.h> 50 51 #include <vfs/hammer2/hammer2_disk.h> 52 #include <vfs/hammer2/hammer2_xxhash.h> 53 54 #include "mkfs_hammer2.h" 55 #include "hammer2_subs.h" 56 57 static uint64_t nowtime(void); 58 static int blkrefary_cmp(const void *b1, const void *b2); 59 static void alloc_direct(hammer2_off_t *basep, hammer2_blockref_t *bref, 60 size_t bytes); 61 62 static int 63 get_hammer2_version(void) 64 { 65 int version = HAMMER2_VOL_VERSION_DEFAULT; 66 size_t olen = sizeof(version); 67 68 if (sysctlbyname("vfs.hammer2.supported_version", 69 &version, &olen, NULL, 0) == 0) { 70 if (version >= HAMMER2_VOL_VERSION_WIP) { 71 version = HAMMER2_VOL_VERSION_WIP - 1; 72 fprintf(stderr, 73 "newfs_hammer2: WARNING: HAMMER2 VFS " 74 "supports higher version than I " 75 "understand.\n" 76 "Using default version %d\n", 77 version); 78 } 79 } else { 80 fprintf(stderr, 81 "newfs_hammer2: WARNING: HAMMER2 VFS not " 82 "loaded, cannot get version info.\n" 83 "Using default version %d\n", 84 version); 85 } 86 return(version); 87 } 88 89 void 90 hammer2_mkfs_init(hammer2_mkfs_options_t *opt) 91 { 92 uint32_t status; 93 94 memset(opt, 0, sizeof(*opt)); 95 96 opt->Hammer2Version = get_hammer2_version(); 97 opt->Label[opt->NLabels++] = strdup("LOCAL"); 98 opt->CompType = HAMMER2_COMP_DEFAULT; /* LZ4 */ 99 opt->CheckType = HAMMER2_CHECK_DEFAULT; /* xxhash64 */ 100 opt->DefaultLabelType = HAMMER2_LABEL_NONE; 101 102 /* 103 * Generate a filesystem id and lookup the filesystem type 104 */ 105 srandomdev(); 106 uuid_create(&opt->Hammer2_VolFSID, NULL); 107 uuid_create(&opt->Hammer2_SupCLID, NULL); 108 uuid_create(&opt->Hammer2_SupFSID, NULL); 109 uuid_from_string(HAMMER2_UUID_STRING, &opt->Hammer2_FSType, &status); 110 /*uuid_name_lookup(&Hammer2_FSType, "DragonFly HAMMER2", &status);*/ 111 if (status != uuid_s_ok) { 112 errx(1, "uuids file does not have the DragonFly " 113 "HAMMER2 filesystem type"); 114 } 115 } 116 117 void 118 hammer2_mkfs_cleanup(hammer2_mkfs_options_t *opt) 119 { 120 int i; 121 122 for (i = 0; i < opt->NLabels; i++) 123 free(opt->Label[i]); 124 } 125 126 static void 127 adjust_options(hammer2_ondisk_t *fso, hammer2_mkfs_options_t *opt) 128 { 129 /* 130 * Adjust Label[] and NLabels. 131 */ 132 switch (opt->DefaultLabelType) { 133 case HAMMER2_LABEL_BOOT: 134 opt->Label[opt->NLabels++] = strdup("BOOT"); 135 break; 136 case HAMMER2_LABEL_ROOT: 137 opt->Label[opt->NLabels++] = strdup("ROOT"); 138 break; 139 case HAMMER2_LABEL_DATA: 140 opt->Label[opt->NLabels++] = strdup("DATA"); 141 break; 142 case HAMMER2_LABEL_NONE: 143 /* nothing to do */ 144 break; 145 default: 146 assert(0); 147 break; 148 } 149 150 /* 151 * Calculate defaults for the boot area size and round to the 152 * volume alignment boundary. 153 * 154 * NOTE: These areas are currently not used for booting but are 155 * reserved for future filesystem expansion. 156 */ 157 hammer2_off_t BootAreaSize = opt->BootAreaSize; 158 if (BootAreaSize == 0) { 159 BootAreaSize = HAMMER2_BOOT_NOM_BYTES; 160 while (BootAreaSize > fso->total_size / 20) 161 BootAreaSize >>= 1; 162 if (BootAreaSize < HAMMER2_BOOT_MIN_BYTES) 163 BootAreaSize = HAMMER2_BOOT_MIN_BYTES; 164 } else if (BootAreaSize < HAMMER2_BOOT_MIN_BYTES) { 165 BootAreaSize = HAMMER2_BOOT_MIN_BYTES; 166 } 167 BootAreaSize = (BootAreaSize + HAMMER2_VOLUME_ALIGNMASK64) & 168 ~HAMMER2_VOLUME_ALIGNMASK64; 169 opt->BootAreaSize = BootAreaSize; 170 171 /* 172 * Calculate defaults for the aux area size and round to the 173 * volume alignment boundary. 174 * 175 * NOTE: These areas are currently not used for logging but are 176 * reserved for future filesystem expansion. 177 */ 178 hammer2_off_t AuxAreaSize = opt->AuxAreaSize; 179 if (AuxAreaSize == 0) { 180 AuxAreaSize = HAMMER2_AUX_NOM_BYTES; 181 while (AuxAreaSize > fso->total_size / 20) 182 AuxAreaSize >>= 1; 183 if (AuxAreaSize < HAMMER2_AUX_MIN_BYTES) 184 AuxAreaSize = HAMMER2_AUX_MIN_BYTES; 185 } else if (AuxAreaSize < HAMMER2_AUX_MIN_BYTES) { 186 AuxAreaSize = HAMMER2_AUX_MIN_BYTES; 187 } 188 AuxAreaSize = (AuxAreaSize + HAMMER2_VOLUME_ALIGNMASK64) & 189 ~HAMMER2_VOLUME_ALIGNMASK64; 190 opt->AuxAreaSize = AuxAreaSize; 191 } 192 193 /* 194 * Convert a string to a 64 bit signed integer with various requirements. 195 */ 196 int64_t 197 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2) 198 { 199 int64_t val; 200 char *ptr; 201 202 val = strtoll(str, &ptr, 0); 203 switch(*ptr) { 204 case 't': 205 case 'T': 206 val *= 1024; 207 /* fall through */ 208 case 'g': 209 case 'G': 210 val *= 1024; 211 /* fall through */ 212 case 'm': 213 case 'M': 214 val *= 1024; 215 /* fall through */ 216 case 'k': 217 case 'K': 218 val *= 1024; 219 break; 220 default: 221 errx(1, "Unknown suffix in number '%s'", str); 222 /* not reached */ 223 } 224 if (ptr[1]) { 225 errx(1, "Unknown suffix in number '%s'", str); 226 /* not reached */ 227 } 228 if (val < minval) { 229 errx(1, "Value too small: %s, min is %s", 230 str, sizetostr(minval)); 231 /* not reached */ 232 } 233 if (val > maxval) { 234 errx(1, "Value too large: %s, max is %s", 235 str, sizetostr(maxval)); 236 /* not reached */ 237 } 238 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) { 239 errx(1, "Value not power of 2: %s", str); 240 /* not reached */ 241 } 242 if ((powerof2 & 2) && (val & HAMMER2_NEWFS_ALIGNMASK)) { 243 errx(1, "Value not an integral multiple of %dK: %s", 244 HAMMER2_NEWFS_ALIGN / 1024, str); 245 /* not reached */ 246 } 247 return(val); 248 } 249 250 static uint64_t 251 nowtime(void) 252 { 253 struct timeval tv; 254 uint64_t xtime; 255 256 gettimeofday(&tv, NULL); 257 xtime = tv.tv_sec * 1000000LL + tv.tv_usec; 258 return(xtime); 259 } 260 261 static hammer2_off_t 262 format_hammer2_misc(hammer2_volume_t *vol, hammer2_mkfs_options_t *opt, 263 hammer2_off_t boot_base, hammer2_off_t aux_base) 264 { 265 char *buf = malloc(HAMMER2_PBUFSIZE); 266 hammer2_off_t alloc_base = aux_base + opt->AuxAreaSize; 267 hammer2_off_t tmp_base; 268 size_t n; 269 int i; 270 271 /* 272 * Clear the entire 4MB reserve for the first 2G zone. 273 */ 274 bzero(buf, HAMMER2_PBUFSIZE); 275 tmp_base = 0; 276 for (i = 0; i < HAMMER2_ZONE_BLOCKS_SEG; ++i) { 277 n = pwrite(vol->fd, buf, HAMMER2_PBUFSIZE, tmp_base); 278 if (n != HAMMER2_PBUFSIZE) { 279 perror("write"); 280 exit(1); 281 } 282 tmp_base += HAMMER2_PBUFSIZE; 283 } 284 285 /* 286 * Make sure alloc_base won't cross the reserved area at the 287 * beginning of each 1GB. 288 * 289 * Reserve space for the super-root inode and the root inode. 290 * Make sure they are in the same 64K block to simplify our code. 291 */ 292 assert((alloc_base & HAMMER2_PBUFMASK) == 0); 293 assert(alloc_base < HAMMER2_FREEMAP_LEVEL1_SIZE); 294 295 /* 296 * Clear the boot/aux area. 297 */ 298 for (tmp_base = boot_base; tmp_base < alloc_base; 299 tmp_base += HAMMER2_PBUFSIZE) { 300 n = pwrite(vol->fd, buf, HAMMER2_PBUFSIZE, tmp_base); 301 if (n != HAMMER2_PBUFSIZE) { 302 perror("write (boot/aux)"); 303 exit(1); 304 } 305 } 306 307 free(buf); 308 return(alloc_base); 309 } 310 311 static hammer2_off_t 312 format_hammer2_inode(hammer2_volume_t *vol, hammer2_mkfs_options_t *opt, 313 hammer2_blockref_t *sroot_blockrefp, 314 hammer2_off_t alloc_base) 315 { 316 char *buf = malloc(HAMMER2_PBUFSIZE); 317 hammer2_inode_data_t *rawip; 318 hammer2_blockref_t sroot_blockref; 319 hammer2_blockref_t root_blockref[MAXLABELS]; 320 uint64_t now; 321 size_t n; 322 int i; 323 324 bzero(buf, HAMMER2_PBUFSIZE); 325 bzero(&sroot_blockref, sizeof(sroot_blockref)); 326 bzero(root_blockref, sizeof(root_blockref)); 327 now = nowtime(); 328 alloc_base &= ~HAMMER2_PBUFMASK64; 329 alloc_direct(&alloc_base, &sroot_blockref, HAMMER2_INODE_BYTES); 330 331 for (i = 0; i < opt->NLabels; ++i) { 332 uuid_create(&opt->Hammer2_PfsCLID[i], NULL); 333 uuid_create(&opt->Hammer2_PfsFSID[i], NULL); 334 335 alloc_direct(&alloc_base, &root_blockref[i], 336 HAMMER2_INODE_BYTES); 337 assert(((sroot_blockref.data_off ^ root_blockref[i].data_off) & 338 ~HAMMER2_PBUFMASK64) == 0); 339 340 /* 341 * Format the root directory inode, which is left empty. 342 */ 343 rawip = (void *)(buf + (HAMMER2_OFF_MASK_LO & 344 root_blockref[i].data_off)); 345 rawip->meta.version = HAMMER2_INODE_VERSION_ONE; 346 rawip->meta.ctime = now; 347 rawip->meta.mtime = now; 348 /* rawip->atime = now; NOT IMPL MUST BE ZERO */ 349 rawip->meta.btime = now; 350 rawip->meta.type = HAMMER2_OBJTYPE_DIRECTORY; 351 rawip->meta.mode = 0755; 352 rawip->meta.inum = 1; /* root inode, inumber 1 */ 353 rawip->meta.nlinks = 1; /* directory link count compat */ 354 355 rawip->meta.name_len = strlen(opt->Label[i]); 356 bcopy(opt->Label[i], rawip->filename, rawip->meta.name_len); 357 rawip->meta.name_key = 358 dirhash((char *)rawip->filename, rawip->meta.name_len); 359 360 /* 361 * Compression mode and supported copyids. 362 * 363 * Do not allow compression when creating any "BOOT" label 364 * (pfs-create also does the same if the pfs is named "BOOT") 365 */ 366 if (strcasecmp(opt->Label[i], "BOOT") == 0) { 367 rawip->meta.comp_algo = HAMMER2_ENC_ALGO( 368 HAMMER2_COMP_AUTOZERO); 369 rawip->meta.check_algo = HAMMER2_ENC_ALGO( 370 HAMMER2_CHECK_XXHASH64); 371 } else { 372 rawip->meta.comp_algo = HAMMER2_ENC_ALGO( 373 opt->CompType); 374 rawip->meta.check_algo = HAMMER2_ENC_ALGO( 375 opt->CheckType); 376 } 377 378 /* 379 * NOTE: We leave nmasters set to 0, which means that we 380 * don't know how many masters there are. The quorum 381 * calculation will effectively be 1 ( 0 / 2 + 1 ). 382 */ 383 rawip->meta.pfs_clid = opt->Hammer2_PfsCLID[i]; 384 rawip->meta.pfs_fsid = opt->Hammer2_PfsFSID[i]; 385 rawip->meta.pfs_type = HAMMER2_PFSTYPE_MASTER; 386 rawip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT; 387 388 /* first allocatable inode number */ 389 rawip->meta.pfs_inum = 16; 390 391 /* rawip->u.blockset is left empty */ 392 393 /* 394 * The root blockref will be stored in the super-root inode as 395 * one of the ~4 PFS root directories. The copyid here is the 396 * actual copyid of the storage ref. 397 * 398 * The key field for a PFS root directory's blockref is 399 * essentially the name key for the entry. 400 */ 401 root_blockref[i].key = rawip->meta.name_key; 402 root_blockref[i].copyid = HAMMER2_COPYID_LOCAL; 403 root_blockref[i].keybits = 0; 404 root_blockref[i].check.xxhash64.value = 405 XXH64(rawip, sizeof(*rawip), XXH_HAMMER2_SEED); 406 root_blockref[i].type = HAMMER2_BREF_TYPE_INODE; 407 root_blockref[i].methods = 408 HAMMER2_ENC_CHECK(HAMMER2_CHECK_XXHASH64) | 409 HAMMER2_ENC_COMP(HAMMER2_COMP_NONE); 410 root_blockref[i].mirror_tid = 16; 411 root_blockref[i].flags = HAMMER2_BREF_FLAG_PFSROOT; 412 } 413 414 /* 415 * Format the super-root directory inode, giving it ~4 PFS root 416 * directories (root_blockref). 417 * 418 * The superroot contains ~4 directories pointing at the PFS root 419 * inodes (named via the label). Inodes contain one blockset which 420 * is fully associative so we can put the entry anywhere without 421 * having to worry about the hash. Use index 0. 422 */ 423 rawip = (void *)(buf + (HAMMER2_OFF_MASK_LO & sroot_blockref.data_off)); 424 rawip->meta.version = HAMMER2_INODE_VERSION_ONE; 425 rawip->meta.ctime = now; 426 rawip->meta.mtime = now; 427 /* rawip->meta.atime = now; NOT IMPL MUST BE ZERO */ 428 rawip->meta.btime = now; 429 rawip->meta.type = HAMMER2_OBJTYPE_DIRECTORY; 430 rawip->meta.mode = 0700; /* super-root - root only */ 431 rawip->meta.inum = 0; /* super root inode, inumber 0 */ 432 rawip->meta.nlinks = 2; /* directory link count compat */ 433 434 rawip->meta.comp_algo = HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO); 435 rawip->meta.check_algo = HAMMER2_ENC_ALGO(HAMMER2_CHECK_XXHASH64); 436 437 /* 438 * The super-root is flagged as a PFS and typically given its own 439 * random FSID, making it possible to mirror an entire HAMMER2 disk 440 * snapshots and all if desired. PFS ids are used to match up 441 * mirror sources and targets and cluster copy sources and targets. 442 * 443 * (XXX whole-disk logical mirroring is not really supported in 444 * the first attempt because each PFS is in its own modify/mirror 445 * transaction id domain, so normal mechanics cannot cross a PFS 446 * boundary). 447 */ 448 rawip->meta.pfs_clid = opt->Hammer2_SupCLID; 449 rawip->meta.pfs_fsid = opt->Hammer2_SupFSID; 450 rawip->meta.pfs_type = HAMMER2_PFSTYPE_SUPROOT; 451 snprintf((char*)rawip->filename, sizeof(rawip->filename), "SUPROOT"); 452 rawip->meta.name_key = 0; 453 rawip->meta.name_len = strlen((char*)rawip->filename); 454 455 /* The super-root has an inode number of 0 */ 456 rawip->meta.pfs_inum = 0; 457 458 /* 459 * Currently newfs_hammer2 just throws the PFS inodes into the 460 * top-level block table at the volume root and doesn't try to 461 * create an indirect block, so we are limited to ~4 at filesystem 462 * creation time. More can be added after mounting. 463 */ 464 qsort(root_blockref, opt->NLabels, sizeof(root_blockref[0]), blkrefary_cmp); 465 for (i = 0; i < opt->NLabels; ++i) 466 rawip->u.blockset.blockref[i] = root_blockref[i]; 467 468 /* 469 * The sroot blockref will be stored in the volume header. 470 */ 471 sroot_blockref.copyid = HAMMER2_COPYID_LOCAL; 472 sroot_blockref.keybits = 0; 473 sroot_blockref.check.xxhash64.value = 474 XXH64(rawip, sizeof(*rawip), XXH_HAMMER2_SEED); 475 sroot_blockref.type = HAMMER2_BREF_TYPE_INODE; 476 sroot_blockref.methods = HAMMER2_ENC_CHECK(HAMMER2_CHECK_XXHASH64) | 477 HAMMER2_ENC_COMP(HAMMER2_COMP_AUTOZERO); 478 sroot_blockref.mirror_tid = 16; 479 rawip = NULL; 480 481 /* 482 * Write out the 64K HAMMER2 block containing the root and sroot. 483 */ 484 assert((sroot_blockref.data_off & ~HAMMER2_PBUFMASK64) == 485 ((alloc_base - 1) & ~HAMMER2_PBUFMASK64)); 486 n = pwrite(vol->fd, buf, HAMMER2_PBUFSIZE, 487 sroot_blockref.data_off & ~HAMMER2_PBUFMASK64); 488 if (n != HAMMER2_PBUFSIZE) { 489 perror("write"); 490 exit(1); 491 } 492 *sroot_blockrefp = sroot_blockref; 493 494 free(buf); 495 return(alloc_base); 496 } 497 498 /* 499 * Create the volume header, the super-root directory inode, and 500 * the writable snapshot subdirectory (named via the label) which 501 * is to be the initial mount point, or at least the first mount point. 502 * newfs_hammer2 doesn't format the freemap bitmaps for these. 503 * 504 * 0 4MB 505 * [----reserved_area----][boot_area][aux_area] 506 * [[vol_hdr][freemap]...] [sroot][root][root]... 507 * \ ^\ ^ ^ 508 * \--------------------------------------/ \---/-----/---... 509 * 510 * NOTE: The total size is 8MB-aligned to avoid edge cases. 511 */ 512 static void 513 format_hammer2(hammer2_ondisk_t *fso, hammer2_mkfs_options_t *opt, int index) 514 { 515 char *buf = malloc(HAMMER2_PBUFSIZE); 516 hammer2_volume_t *vol = &fso->volumes[index]; 517 hammer2_volume_data_t *voldata; 518 hammer2_blockset_t sroot_blockset; 519 hammer2_off_t boot_base = HAMMER2_ZONE_SEG; 520 hammer2_off_t aux_base = boot_base + opt->BootAreaSize; 521 hammer2_off_t alloc_base; 522 size_t n; 523 int i; 524 525 /* 526 * Make sure we can write to the last usable block. 527 */ 528 bzero(buf, HAMMER2_PBUFSIZE); 529 n = pwrite(vol->fd, buf, HAMMER2_PBUFSIZE, 530 vol->size - HAMMER2_PBUFSIZE); 531 if (n != HAMMER2_PBUFSIZE) { 532 perror("write (at-end-of-volume)"); 533 exit(1); 534 } 535 536 /* 537 * Format misc area and sroot/root inodes for the root volume. 538 */ 539 bzero(&sroot_blockset, sizeof(sroot_blockset)); 540 if (vol->id == HAMMER2_ROOT_VOLUME) { 541 alloc_base = format_hammer2_misc(vol, opt, boot_base, aux_base); 542 alloc_base = format_hammer2_inode(vol, opt, 543 &sroot_blockset.blockref[0], 544 alloc_base); 545 } else { 546 alloc_base = 0; 547 for (i = 0; i < HAMMER2_SET_COUNT; ++i) 548 sroot_blockset.blockref[i].type = HAMMER2_BREF_TYPE_INVALID; 549 } 550 551 /* 552 * Format the volume header. 553 * 554 * The volume header points to sroot_blockset. Also be absolutely 555 * sure that allocator_beg is set for the root volume. 556 */ 557 assert(HAMMER2_VOLUME_BYTES <= HAMMER2_PBUFSIZE); 558 bzero(buf, HAMMER2_PBUFSIZE); 559 voldata = (void *)buf; 560 561 voldata->magic = HAMMER2_VOLUME_ID_HBO; 562 if (vol->id == HAMMER2_ROOT_VOLUME) { 563 voldata->boot_beg = boot_base; 564 voldata->boot_end = boot_base + opt->BootAreaSize; 565 voldata->aux_beg = aux_base; 566 voldata->aux_end = aux_base + opt->AuxAreaSize; 567 } 568 voldata->volu_size = vol->size; 569 voldata->version = opt->Hammer2Version; 570 voldata->flags = 0; 571 572 if (voldata->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES) { 573 voldata->volu_id = vol->id; 574 voldata->nvolumes = fso->nvolumes; 575 voldata->total_size = fso->total_size; 576 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 577 if (i < fso->nvolumes) 578 voldata->volu_loff[i] = fso->volumes[i].offset; 579 else 580 voldata->volu_loff[i] = (hammer2_off_t)-1; 581 } 582 } 583 584 voldata->fsid = opt->Hammer2_VolFSID; 585 voldata->fstype = opt->Hammer2_FSType; 586 587 voldata->peer_type = DMSG_PEER_HAMMER2; /* LNK_CONN identification */ 588 589 assert(vol->id == HAMMER2_ROOT_VOLUME || alloc_base == 0); 590 voldata->allocator_size = fso->free_size; 591 if (vol->id == HAMMER2_ROOT_VOLUME) { 592 voldata->allocator_free = fso->free_size; 593 voldata->allocator_beg = alloc_base; 594 } 595 596 voldata->sroot_blockset = sroot_blockset; 597 voldata->mirror_tid = 16; /* all blockref mirror TIDs set to 16 */ 598 voldata->freemap_tid = 16; /* all blockref mirror TIDs set to 16 */ 599 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1] = 600 hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRC1_OFF, 601 HAMMER2_VOLUME_ICRC1_SIZE); 602 603 /* 604 * Set ICRC_SECT0 after all remaining elements of sect0 have been 605 * populated in the volume header. Note hat ICRC_SECT* (except for 606 * SECT0) are part of sect0. 607 */ 608 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0] = 609 hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRC0_OFF, 610 HAMMER2_VOLUME_ICRC0_SIZE); 611 voldata->icrc_volheader = 612 hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRCVH_OFF, 613 HAMMER2_VOLUME_ICRCVH_SIZE); 614 615 /* 616 * Write the volume header and all alternates. 617 */ 618 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 619 if (i * HAMMER2_ZONE_BYTES64 >= vol->size) 620 break; 621 n = pwrite(vol->fd, buf, HAMMER2_PBUFSIZE, 622 i * HAMMER2_ZONE_BYTES64); 623 if (n != HAMMER2_PBUFSIZE) { 624 perror("write"); 625 exit(1); 626 } 627 } 628 fsync(vol->fd); 629 630 /* 631 * Cleanup 632 */ 633 free(buf); 634 } 635 636 static void 637 alloc_direct(hammer2_off_t *basep, hammer2_blockref_t *bref, size_t bytes) 638 { 639 int radix; 640 641 radix = 0; 642 assert(bytes); 643 while ((bytes & 1) == 0) { 644 bytes >>= 1; 645 ++radix; 646 } 647 assert(bytes == 1); 648 if (radix < HAMMER2_RADIX_MIN) 649 radix = HAMMER2_RADIX_MIN; 650 651 bzero(bref, sizeof(*bref)); 652 bref->data_off = *basep | radix; 653 bref->vradix = radix; 654 655 *basep += 1U << radix; 656 } 657 658 static int 659 blkrefary_cmp(const void *b1, const void *b2) 660 { 661 const hammer2_blockref_t *bref1 = b1; 662 const hammer2_blockref_t *bref2 = b2; 663 664 if (bref1->key < bref2->key) 665 return(-1); 666 if (bref1->key > bref2->key) 667 return(1); 668 return 0; 669 } 670 671 void 672 hammer2_mkfs(int ac, char **av, hammer2_mkfs_options_t *opt) 673 { 674 hammer2_off_t resid = 0, reserved_size; 675 hammer2_ondisk_t fso; 676 int i; 677 char *vol_fsid = NULL; 678 char *sup_clid_name = NULL; 679 char *sup_fsid_name = NULL; 680 char *pfs_clid_name = NULL; 681 char *pfs_fsid_name = NULL; 682 683 /* 684 * Sanity check basic filesystem structures. No cookies for us 685 * if it gets broken! 686 */ 687 assert(sizeof(hammer2_volume_data_t) == HAMMER2_VOLUME_BYTES); 688 assert(sizeof(hammer2_inode_data_t) == HAMMER2_INODE_BYTES); 689 assert(sizeof(hammer2_blockref_t) == HAMMER2_BLOCKREF_BYTES); 690 691 /* 692 * Construct volumes information. 693 * 1GB alignment (level1 freemap size) for volumes except for the last. 694 * For the last volume, typically 8MB alignment to avoid edge cases for 695 * reserved blocks and so raid stripes (if any) operate efficiently. 696 */ 697 hammer2_init_ondisk(&fso); 698 fso.version = opt->Hammer2Version; 699 fso.nvolumes = ac; 700 701 assert(ac >= 1); 702 if (opt->NFileSystemSizes == 1) { 703 resid = opt->FileSystemSize[0]; 704 assert(resid >= HAMMER2_FREEMAP_LEVEL1_SIZE); 705 } else if (opt->NFileSystemSizes > 1) { 706 if (ac != opt->NFileSystemSizes) 707 errx(1, "Invalid filesystem size count %d vs %d", 708 opt->NFileSystemSizes, ac); 709 } 710 711 for (i = 0; i < fso.nvolumes; ++i) { 712 hammer2_volume_t *vol = &fso.volumes[i]; 713 hammer2_off_t size; 714 int fd = open(av[i], O_RDWR); 715 if (fd < 0) 716 err(1, "Unable to open %s R+W", av[i]); 717 size = check_volume(fd); 718 719 /* 720 * Limit size if a smaller filesystem size is specified. 721 */ 722 if (opt->NFileSystemSizes == 1) { 723 if (resid == 0) 724 errx(1, "No remaining filesystem size for %s", 725 av[i]); 726 if (size > resid) 727 size = resid; 728 resid -= size; 729 } else if (opt->NFileSystemSizes > 1) { 730 resid = opt->FileSystemSize[i]; 731 assert(resid >= HAMMER2_FREEMAP_LEVEL1_SIZE); 732 if (size > resid) 733 size = resid; 734 } 735 736 if (i == fso.nvolumes - 1) 737 size &= ~HAMMER2_VOLUME_ALIGNMASK64; 738 else 739 size &= ~HAMMER2_FREEMAP_LEVEL1_MASK; 740 if (size == 0) 741 errx(1, "%s has aligned size of 0", av[i]); 742 hammer2_install_volume(vol, fd, i, av[i], fso.total_size, size); 743 fso.total_size += size; 744 } 745 746 /* 747 * Verify volumes constructed above. 748 */ 749 for (i = 0; i < fso.nvolumes; ++i) { 750 hammer2_volume_t *vol = &fso.volumes[i]; 751 printf("Volume %-15s size %s\n", vol->path, 752 sizetostr(vol->size)); 753 } 754 hammer2_verify_volumes(&fso, NULL); 755 756 /* 757 * Adjust options. 758 */ 759 adjust_options(&fso, opt); 760 761 /* 762 * We'll need to stuff this in the volume header soon. 763 */ 764 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &vol_fsid); 765 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &sup_clid_name); 766 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &sup_fsid_name); 767 768 /* 769 * Calculate the amount of reserved space. HAMMER2_ZONE_SEG (4MB) 770 * is reserved at the beginning of every 1GB of storage, rounded up. 771 * Thus a 200MB filesystem will still have a 4MB reserve area. 772 * 773 * We also include the boot and aux areas in the reserve. The 774 * reserve is used to help 'df' calculate the amount of available 775 * space. 776 * 777 * XXX I kinda screwed up and made the reserved area on the LEVEL1 778 * boundary rather than the ZONE boundary. LEVEL1 is on 1GB 779 * boundaries rather than 2GB boundaries. Stick with the LEVEL1 780 * boundary. 781 */ 782 reserved_size = ((fso.total_size + HAMMER2_FREEMAP_LEVEL1_MASK) / 783 HAMMER2_FREEMAP_LEVEL1_SIZE) * HAMMER2_ZONE_SEG64; 784 785 fso.free_size = fso.total_size - reserved_size - opt->BootAreaSize - opt->AuxAreaSize; 786 if ((int64_t)fso.free_size < 0) { 787 fprintf(stderr, "Not enough free space\n"); 788 exit(1); 789 } 790 791 /* 792 * Format HAMMER2 volumes. 793 */ 794 for (i = 0; i < fso.nvolumes; ++i) 795 format_hammer2(&fso, opt, i); 796 797 printf("---------------------------------------------\n"); 798 printf("version: %d\n", opt->Hammer2Version); 799 printf("total-size: %s (%jd bytes)\n", 800 sizetostr(fso.total_size), 801 (intmax_t)fso.total_size); 802 printf("boot-area-size: %s (%jd bytes)\n", 803 sizetostr(opt->BootAreaSize), 804 (intmax_t)opt->BootAreaSize); 805 printf("aux-area-size: %s (%jd bytes)\n", 806 sizetostr(opt->AuxAreaSize), 807 (intmax_t)opt->AuxAreaSize); 808 printf("topo-reserved: %s (%jd bytes)\n", 809 sizetostr(reserved_size), 810 (intmax_t)reserved_size); 811 printf("free-size: %s (%jd bytes)\n", 812 sizetostr(fso.free_size), 813 (intmax_t)fso.free_size); 814 printf("vol-fsid: %s\n", vol_fsid); 815 printf("sup-clid: %s\n", sup_clid_name); 816 printf("sup-fsid: %s\n", sup_fsid_name); 817 for (i = 0; i < opt->NLabels; ++i) { 818 printf("PFS \"%s\"\n", opt->Label[i]); 819 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &pfs_clid_name); 820 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &pfs_fsid_name); 821 printf(" clid %s\n", pfs_clid_name); 822 printf(" fsid %s\n", pfs_fsid_name); 823 } 824 if (opt->DebugOpt) { 825 printf("---------------------------------------------\n"); 826 hammer2_print_volumes(&fso); 827 } 828 829 free(vol_fsid); 830 free(sup_clid_name); 831 free(sup_fsid_name); 832 free(pfs_clid_name); 833 free(pfs_fsid_name); 834 835 for (i = 0; i < fso.nvolumes; ++i) 836 hammer2_uninstall_volume(&fso.volumes[i]); 837 } 838