1 /* 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org> 5 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved. 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 35 #if HAVE_NBTOOL_CONFIG_H 36 #include "nbtool_config.h" 37 #endif 38 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 #include <sys/sysctl.h> 42 #include <sys/mman.h> 43 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <stdbool.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 #include <time.h> 51 #include <err.h> 52 #include <assert.h> 53 #include <util.h> 54 55 #include "makefs.h" 56 #include "hammer2.h" 57 58 #define APRINTF(X, ...) \ 59 printf("%s: " X, __func__, ## __VA_ARGS__) 60 61 static void hammer2_dump_fsinfo(fsinfo_t *); 62 static int hammer2_create_image(const char *, fsinfo_t *); 63 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *, 64 fsnode *, fsinfo_t *, int); 65 static void hammer2_validate(const char *, fsnode *, fsinfo_t *); 66 static void hammer2_size_dir(fsnode *, fsinfo_t *); 67 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *); 68 static int hammer2_bulkfree(struct m_vnode *vp); 69 static int hammer2_destroy_path(struct m_vnode *vp, const char *f); 70 static int hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum); 71 static int hammer2_growfs(struct m_vnode *vp, hammer2_off_t size); 72 73 fsnode *hammer2_curnode; 74 75 void 76 hammer2_prep_opts(fsinfo_t *fsopts) 77 { 78 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt)); 79 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 80 81 const option_t hammer2_options[] = { 82 /* newfs_hammer2(8) compatible options */ 83 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" }, 84 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" }, 85 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" }, 86 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" }, 87 /* makefs(8) specific options */ 88 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" }, 89 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32, 90 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" }, 91 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" }, 92 { 'B', "Bulkfree", &h2_opt->bulkfree, OPT_BOOL, 0, 0, "offline bulkfree" }, 93 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" }, 94 { 'G', "Growfs", &h2_opt->growfs, OPT_BOOL, 0, 0, "offline growfs" }, 95 { .name = NULL }, 96 }; 97 98 hammer2_mkfs_init(opt); 99 100 /* make this tunable ? */ 101 assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT); 102 assert(opt->CheckType == HAMMER2_CHECK_XXHASH64); 103 104 /* force debug mode for mkfs */ 105 opt->DebugOpt = 1; 106 107 fsopts->fs_specific = h2_opt; 108 fsopts->fs_options = copy_opts(hammer2_options); 109 fsopts->sectorsize = DEV_BSIZE; 110 } 111 112 void 113 hammer2_cleanup_opts(fsinfo_t *fsopts) 114 { 115 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 116 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 117 118 hammer2_mkfs_cleanup(opt); 119 120 free(h2_opt); 121 free(fsopts->fs_options); 122 } 123 124 int 125 hammer2_parse_opts(const char *option, fsinfo_t *fsopts) 126 { 127 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 128 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 129 130 option_t *hammer2_options = fsopts->fs_options; 131 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */ 132 int i; 133 134 assert(option != NULL); 135 assert(fsopts != NULL); 136 137 if (debug & DEBUG_FS_PARSE_OPTS) 138 APRINTF("got `%s'\n", option); 139 140 i = set_option(hammer2_options, option, buf, sizeof(buf)); 141 if (i == -1) 142 return 0; 143 144 if (hammer2_options[i].name == NULL) 145 abort(); 146 147 switch (hammer2_options[i].letter) { 148 case 'b': 149 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 150 HAMMER2_BOOT_MAX_BYTES, 2); 151 break; 152 case 'r': 153 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 154 HAMMER2_AUX_MAX_BYTES, 2); 155 break; 156 case 'V': 157 opt->Hammer2Version = strtol(buf, NULL, 0); 158 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN || 159 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP) 160 errx(1, "I don't understand how to format " 161 "HAMMER2 version %d", 162 opt->Hammer2Version); 163 break; 164 case 'L': 165 h2_opt->label_specified = 1; 166 if (strcasecmp(buf, "none") == 0) 167 break; 168 if (opt->NLabels >= MAXLABELS) 169 errx(1, "Limit of %d local labels", MAXLABELS - 1); 170 if (strlen(buf) == 0) 171 errx(1, "Volume label '%s' cannot be 0-length", buf); 172 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 173 errx(1, "Volume label '%s' is too long (%d chars max)", 174 buf, HAMMER2_INODE_MAXNAME - 1); 175 opt->Label[opt->NLabels++] = strdup(buf); 176 break; 177 case 'm': 178 if (strlen(buf) == 0) 179 errx(1, "Volume label '%s' cannot be 0-length", buf); 180 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 181 errx(1, "Volume label '%s' is too long (%d chars max)", 182 buf, HAMMER2_INODE_MAXNAME - 1); 183 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label)); 184 break; 185 case 'd': 186 hammer2_debug = strtoll(buf, NULL, 0); 187 break; 188 case 'D': 189 h2_opt->destroy = 1; 190 if (buf[0] == '/') { 191 strlcpy(h2_opt->destroy_path, buf + 1, 192 sizeof(h2_opt->destroy_path)); 193 } else if ((buf[0] == '0' && buf[1] == 'x') || 194 (buf[0] >= '0' && buf[0] <= '9')) { 195 h2_opt->destroy_inum = strtoull(buf, NULL, 0); 196 if (errno) 197 err(1, "strtoull"); 198 } else { 199 errx(1, "Invalid destroy argument %s", buf); 200 } 201 break; 202 default: 203 break; 204 } 205 206 return 1; 207 } 208 209 void 210 hammer2_makefs(const char *image, const char *dir, fsnode *root, 211 fsinfo_t *fsopts) 212 { 213 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 214 struct mount mp; 215 struct hammer2_mount_info info; 216 struct m_vnode devvp, *vroot; 217 hammer2_inode_t *iroot; 218 struct timeval start; 219 int error; 220 221 /* ioctl commands could have NULL root */ 222 assert(image != NULL); 223 assert(dir != NULL); 224 assert(fsopts != NULL); 225 226 if (debug & DEBUG_FS_MAKEFS) 227 APRINTF("image \"%s\" directory \"%s\" root %p\n", 228 image, dir, root); 229 230 /* validate tree and options */ 231 TIMER_START(start); 232 hammer2_validate(dir, root, fsopts); 233 TIMER_RESULTS(start, "hammer2_validate"); 234 235 if (!h2_opt->bulkfree && !h2_opt->destroy && !h2_opt->growfs) { 236 /* create image */ 237 TIMER_START(start); 238 if (hammer2_create_image(image, fsopts) == -1) 239 errx(1, "image file `%s' not created", image); 240 TIMER_RESULTS(start, "hammer2_create_image"); 241 } else { 242 /* open existing image */ 243 fsopts->fd = open(image, O_RDWR); 244 if (fsopts->fd < 0) 245 err(1, "failed to open %s", image); 246 } 247 assert(fsopts->fd > 0); 248 249 if (debug & DEBUG_FS_MAKEFS) 250 putchar('\n'); 251 252 /* vfs init */ 253 error = hammer2_vfs_init(); 254 if (error) 255 errx(1, "failed to vfs init, error %d", error); 256 257 /* mount image */ 258 memset(&devvp, 0, sizeof(devvp)); 259 devvp.fs = fsopts; 260 memset(&mp, 0, sizeof(mp)); 261 memset(&info, 0, sizeof(info)); 262 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info); 263 if (error) 264 errx(1, "failed to mount, error %d", error); 265 assert(mp.mnt_data); 266 267 /* get root vnode */ 268 vroot = NULL; 269 error = hammer2_vfs_root(&mp, &vroot); 270 if (error) 271 errx(1, "failed to get root vnode, error %d", error); 272 assert(vroot); 273 274 iroot = VTOI(vroot); 275 assert(iroot); 276 printf("root inode inum %lld, mode 0%o, refs %d\n", 277 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs); 278 279 if (!h2_opt->bulkfree && !h2_opt->destroy && !h2_opt->growfs) { 280 /* populate image */ 281 printf("populating `%s'\n", image); 282 TIMER_START(start); 283 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0)) 284 errx(1, "image file `%s' not populated", image); 285 TIMER_RESULTS(start, "hammer2_populate_dir"); 286 } else if (h2_opt->bulkfree) { 287 /* bulkfree image */ 288 printf("bulkfree `%s'\n", image); 289 TIMER_START(start); 290 if (hammer2_bulkfree(vroot)) 291 errx(1, "bulkfree `%s' failed", image); 292 TIMER_RESULTS(start, "hammer2_bulkfree"); 293 } else if (h2_opt->destroy) { 294 /* destroy inode */ 295 TIMER_START(start); 296 if (strlen(h2_opt->destroy_path)) { 297 printf("destroy `%s' in `%s'\n", 298 h2_opt->destroy_path, image); 299 if (hammer2_destroy_path(vroot, h2_opt->destroy_path)) 300 errx(1, "destroy `%s' in `%s' failed", 301 h2_opt->destroy_path, image); 302 } else { 303 printf("destroy %lld in `%s'\n", 304 (long long)h2_opt->destroy_inum, image); 305 if (hammer2_destroy_inum(vroot, h2_opt->destroy_inum)) 306 errx(1, "destroy %lld in `%s' failed", 307 (long long)h2_opt->destroy_inum, image); 308 } 309 TIMER_RESULTS(start, "hammer2_destroy"); 310 } else if (h2_opt->growfs) { 311 /* growfs image */ 312 printf("growfs `%s'\n", image); 313 TIMER_START(start); 314 if (hammer2_growfs(vroot, h2_opt->image_size)) 315 errx(1, "growfs `%s' failed", image); 316 TIMER_RESULTS(start, "hammer2_growfs"); 317 } 318 319 /* unmount image */ 320 error = hammer2_vfs_unmount(&mp, 0); 321 if (error) 322 errx(1, "failed to unmount, error %d", error); 323 324 /* check leaked resource */ 325 if (vnode_count) 326 printf("XXX %lld vnode left\n", (long long)vnode_count); 327 if (hammer2_chain_allocs) 328 printf("XXX %ld chain left\n", hammer2_chain_allocs); 329 bcleanup(); 330 331 /* vfs uninit */ 332 error = hammer2_vfs_uninit(); 333 if (error) 334 errx(1, "failed to vfs uninit, error %d", error); 335 336 if (close(fsopts->fd) == -1) 337 err(1, "closing `%s'", image); 338 fsopts->fd = -1; 339 340 printf("image `%s' complete\n", image); 341 } 342 343 /* end of public functions */ 344 345 static hammer2_off_t 346 hammer2_image_size(fsinfo_t *fsopts) 347 { 348 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 349 hammer2_off_t image_size, used_size = 0; 350 int num_level1, delta_num_level1; 351 352 /* use 4 volume headers by default */ 353 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */ 354 assert(num_level1 != 0); 355 assert(num_level1 <= 8); 356 357 /* add 4MiB segment for each level1 */ 358 used_size += HAMMER2_ZONE_SEG64 * num_level1; 359 360 /* add boot/aux area, but exact size unknown at this point */ 361 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES; 362 363 /* add data size */ 364 used_size += fsopts->size; 365 366 /* XXX add extra level1 for meta data and indirect blocks */ 367 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 368 369 /* XXX add extra level1 for safety */ 370 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10) 371 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 372 373 /* use 8GiB image size by default */ 374 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1; 375 printf("trying default image size %s\n", sizetostr(image_size)); 376 377 /* adjust if image size isn't large enough */ 378 if (used_size > image_size) { 379 /* determine extra level1 needed */ 380 delta_num_level1 = howmany(used_size - image_size, 381 HAMMER2_FREEMAP_LEVEL1_SIZE); 382 383 /* adjust used size with 4MiB segment for each extra level1 */ 384 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1; 385 386 /* adjust image size with extra level1 */ 387 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1; 388 printf("trying adjusted image size %s\n", 389 sizetostr(image_size)); 390 391 if (used_size > image_size) 392 errx(1, "invalid used_size %lld > image_size %lld", 393 (long long)used_size, (long long)image_size); 394 } 395 396 return image_size; 397 } 398 399 static const char * 400 hammer2_label_name(int label_type) 401 { 402 switch (label_type) { 403 case HAMMER2_LABEL_NONE: 404 return "NONE"; 405 case HAMMER2_LABEL_BOOT: 406 return "BOOT"; 407 case HAMMER2_LABEL_ROOT: 408 return "ROOT"; 409 case HAMMER2_LABEL_DATA: 410 return "DATA"; 411 default: 412 assert(0); 413 } 414 return NULL; 415 } 416 417 static void 418 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) 419 { 420 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 421 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 422 hammer2_off_t image_size = 0, minsize, maxsize; 423 const char *s; 424 425 assert(dir != NULL); 426 assert(fsopts != NULL); 427 428 if (debug & DEBUG_FS_VALIDATE) { 429 APRINTF("before defaults set:\n"); 430 hammer2_dump_fsinfo(fsopts); 431 } 432 433 /* makefs only supports "DATA" for default PFS label */ 434 if (!h2_opt->label_specified) { 435 opt->DefaultLabelType = HAMMER2_LABEL_DATA; 436 s = hammer2_label_name(opt->DefaultLabelType); 437 printf("using default label \"%s\"\n", s); 438 } 439 440 /* set default mount PFS label */ 441 if (!strcmp(h2_opt->mount_label, "")) { 442 s = hammer2_label_name(HAMMER2_LABEL_DATA); 443 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label)); 444 printf("using default mount label \"%s\"\n", s); 445 } 446 447 /* set default number of volume headers */ 448 if (!h2_opt->num_volhdr) { 449 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS; 450 printf("using default %d volume headers\n", h2_opt->num_volhdr); 451 } 452 453 /* done if ioctl commands */ 454 if (h2_opt->bulkfree || h2_opt->destroy) 455 goto done; 456 if (h2_opt->growfs) 457 goto ignore_size_dir; 458 459 /* calculate data size */ 460 if (fsopts->size != 0) 461 fsopts->size = 0; /* shouldn't reach here to begin with */ 462 if (root == NULL) 463 errx(1, "fsnode tree not constructed"); 464 hammer2_size_dir(root, fsopts); 465 printf("estimated data size %s from %lld inode\n", 466 sizetostr(fsopts->size), (long long)fsopts->inodes); 467 468 /* determine image size from data size */ 469 image_size = hammer2_image_size(fsopts); 470 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 471 ignore_size_dir: 472 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 473 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 474 if (image_size < minsize) 475 image_size = minsize; 476 else if (maxsize > 0 && image_size > maxsize) 477 errx(1, "`%s' size of %lld is larger than the maxsize of %lld", 478 dir, (long long)image_size, (long long)maxsize); 479 480 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 481 h2_opt->image_size = image_size; 482 printf("using %s image size\n", sizetostr(h2_opt->image_size)); 483 done: 484 if (debug & DEBUG_FS_VALIDATE) { 485 APRINTF("after defaults set:\n"); 486 hammer2_dump_fsinfo(fsopts); 487 } 488 } 489 490 static void 491 hammer2_dump_fsinfo(fsinfo_t *fsopts) 492 { 493 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 494 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 495 int i; 496 char *s; 497 498 assert(fsopts != NULL); 499 500 APRINTF("fsinfo_t at %p\n", fsopts); 501 502 printf("\tinodes %lld\n", (long long)fsopts->inodes); 503 printf("\tsize %lld, minsize %lld, maxsize %lld\n", 504 (long long)fsopts->size, 505 (long long)fsopts->minsize, 506 (long long)fsopts->maxsize); 507 508 printf("\thammer2_debug 0x%x\n", hammer2_debug); 509 510 printf("\tlabel_specified %d\n", h2_opt->label_specified); 511 printf("\tmount_label \"%s\"\n", h2_opt->mount_label); 512 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr); 513 printf("\tbulkfree %d\n", h2_opt->bulkfree); 514 printf("\tdestroy %d\n", h2_opt->destroy); 515 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path); 516 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum); 517 printf("\tgrowfs %d\n", h2_opt->growfs); 518 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size); 519 520 printf("\tHammer2Version %d\n", opt->Hammer2Version); 521 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize); 522 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize); 523 printf("\tNLabels %d\n", opt->NLabels); 524 printf("\tCompType %d\n", opt->CompType); 525 printf("\tCheckType %d\n", opt->CheckType); 526 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType); 527 printf("\tDebugOpt %d\n", opt->DebugOpt); 528 529 s = NULL; 530 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s); 531 printf("\tHammer2_FSType \"%s\"\n", s); 532 s = NULL; 533 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s); 534 printf("\tHammer2_VolFSID \"%s\"\n", s); 535 s = NULL; 536 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s); 537 printf("\tHammer2_SupCLID \"%s\"\n", s); 538 s = NULL; 539 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s); 540 printf("\tHammer2_SupFSID \"%s\"\n", s); 541 542 for (i = 0; i < opt->NLabels; i++) { 543 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]); 544 s = NULL; 545 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s); 546 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s); 547 s = NULL; 548 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s); 549 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s); 550 } 551 552 free(s); 553 } 554 555 static int 556 hammer2_create_image(const char *image, fsinfo_t *fsopts) 557 { 558 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 559 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 560 char *av[] = { (char *)image, }; /* XXX support multi-volumes */ 561 char *buf; 562 int i, bufsize, oflags; 563 off_t bufrem; 564 565 assert(image != NULL); 566 assert(fsopts != NULL); 567 568 /* create image */ 569 oflags = O_RDWR | O_CREAT; 570 if (fsopts->offset == 0) 571 oflags |= O_TRUNC; 572 if ((fsopts->fd = open(image, oflags, 0666)) == -1) { 573 warn("can't open `%s' for writing", image); 574 return -1; 575 } 576 577 /* zero image */ 578 bufsize = HAMMER2_PBUFSIZE; 579 bufrem = h2_opt->image_size; 580 if (fsopts->sparse) { 581 if (ftruncate(fsopts->fd, bufrem) == -1) { 582 warn("sparse option disabled"); 583 fsopts->sparse = 0; 584 } 585 } 586 if (fsopts->sparse) { 587 /* File truncated at bufrem. Remaining is 0 */ 588 bufrem = 0; 589 buf = NULL; 590 } else { 591 if (debug & DEBUG_FS_CREATE_IMAGE) 592 APRINTF("zero-ing image `%s', %lld sectors, " 593 "using %d byte chunks\n", 594 image, (long long)bufrem, bufsize); 595 buf = ecalloc(1, bufsize); 596 } 597 598 if (fsopts->offset != 0) { 599 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) { 600 warn("can't seek"); 601 free(buf); 602 return -1; 603 } 604 } 605 606 while (bufrem > 0) { 607 i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); 608 if (i == -1) { 609 warn("zeroing image, %lld bytes to go", 610 (long long)bufrem); 611 free(buf); 612 return -1; 613 } 614 bufrem -= i; 615 } 616 if (buf) 617 free(buf); 618 619 /* make the file system */ 620 if (debug & DEBUG_FS_CREATE_IMAGE) 621 APRINTF("calling mkfs(\"%s\", ...)\n", image); 622 hammer2_mkfs(1, av, opt); /* success if returned */ 623 624 return fsopts->fd; 625 } 626 627 static off_t 628 hammer2_phys_size(off_t size) 629 { 630 off_t radix_size, phys_size = 0; 631 int i; 632 633 if (size > HAMMER2_PBUFSIZE) { 634 phys_size += rounddown(size, HAMMER2_PBUFSIZE); 635 size = size % HAMMER2_PBUFSIZE; 636 } 637 638 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) { 639 radix_size = 1UL << i; 640 if (radix_size >= size) { 641 phys_size += radix_size; 642 break; 643 } 644 } 645 646 return phys_size; 647 } 648 649 /* calculate data size */ 650 static void 651 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts) 652 { 653 fsnode *node; 654 655 assert(fsopts != NULL); 656 657 if (debug & DEBUG_FS_SIZE_DIR) 658 APRINTF("entry: bytes %lld inodes %lld\n", 659 (long long)fsopts->size, (long long)fsopts->inodes); 660 661 for (node = root; node != NULL; node = node->next) { 662 if (node == root) { /* we're at "." */ 663 assert(strcmp(node->name, ".") == 0); 664 } else if ((node->inode->flags & FI_SIZED) == 0) { 665 /* don't count duplicate names */ 666 node->inode->flags |= FI_SIZED; 667 if (debug & DEBUG_FS_SIZE_DIR_NODE) 668 APRINTF("`%s' size %lld\n", 669 node->name, 670 (long long)node->inode->st.st_size); 671 fsopts->inodes++; 672 fsopts->size += sizeof(hammer2_inode_data_t); 673 if (node->type == S_IFREG) { 674 size_t st_size = node->inode->st.st_size; 675 if (st_size > HAMMER2_EMBEDDED_BYTES) 676 fsopts->size += hammer2_phys_size(st_size); 677 } else if (node->type == S_IFLNK) { 678 size_t nlen = strlen(node->symlink); 679 if (nlen > HAMMER2_EMBEDDED_BYTES) 680 fsopts->size += hammer2_phys_size(nlen); 681 } 682 } 683 if (node->type == S_IFDIR) 684 hammer2_size_dir(node->child, fsopts); 685 } 686 687 if (debug & DEBUG_FS_SIZE_DIR) 688 APRINTF("exit: size %lld inodes %lld\n", 689 (long long)fsopts->size, (long long)fsopts->inodes); 690 } 691 692 static void 693 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp, 694 const fsnode *node, int depth, const char *msg) 695 { 696 if (debug & DEBUG_FS_POPULATE) { 697 if (1) { 698 int indent = depth * 2; 699 char *type; 700 if (S_ISDIR(node->type)) 701 type = "dir"; 702 else if (S_ISREG(node->type)) 703 type = "reg"; 704 else if (S_ISLNK(node->type)) 705 type = "lnk"; 706 else if (S_ISFIFO(node->type)) 707 type = "fifo"; 708 else 709 type = "???"; 710 printf("%*.*s", indent, indent, ""); 711 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n", 712 dvp, dvp ? VTOI(dvp)->refs : 0, 713 vp, vp ? VTOI(vp)->refs : 0, 714 node->name, type, msg); 715 } else { 716 char type; 717 if (S_ISDIR(node->type)) 718 type = 'd'; 719 else if (S_ISREG(node->type)) 720 type = 'r'; 721 else if (S_ISLNK(node->type)) 722 type = 'l'; 723 else if (S_ISFIFO(node->type)) 724 type = 'f'; 725 else 726 type = '?'; 727 printf("%c", type); 728 fflush(stdout); 729 } 730 } 731 } 732 733 static int 734 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root, 735 fsnode *parent, fsinfo_t *fsopts, int depth) 736 { 737 fsnode *cur; 738 struct m_vnode *vp; 739 struct stat st; 740 char f[MAXPATHLEN]; 741 const char *path; 742 int hardlink; 743 int error; 744 745 assert(dvp != NULL); 746 assert(dir != NULL); 747 assert(root != NULL); 748 assert(parent != NULL); 749 assert(fsopts != NULL); 750 751 /* assert root directory */ 752 assert(S_ISDIR(root->type)); 753 assert(!strcmp(root->name, ".")); 754 assert(!root->child); 755 assert(!root->parent || root->parent->child == root); 756 757 hammer2_print(dvp, NULL, root, depth, "enter"); 758 if (stat(dir, &st) == -1) 759 err(1, "no such path %s", dir); 760 if (!S_ISDIR(st.st_mode)) 761 errx(1, "no such dir %s", dir); 762 763 for (cur = root->next; cur != NULL; cur = cur->next) { 764 /* global variable for HAMMER2 vnops */ 765 hammer2_curnode = cur; 766 767 /* construct source path */ 768 if (cur->contents) { 769 path = cur->contents; 770 } else { 771 if (S_ISDIR(cur->type)) { 772 /* this should be same as root/path/name */ 773 if (snprintf(f, sizeof(f), "%s/%s", 774 dir, cur->name) >= (int)sizeof(f)) 775 errx(1, "path %s too long", f); 776 } else { 777 if (snprintf(f, sizeof(f), "%s/%s/%s", 778 cur->root, cur->path, cur->name) >= (int)sizeof(f)) 779 errx(1, "path %s too long", f); 780 } 781 path = f; 782 } 783 if (stat(path, &st) == -1) 784 err(1, "no such file %s", f); 785 786 /* update node state */ 787 if ((cur->inode->flags & FI_ALLOCATED) == 0) { 788 cur->inode->flags |= FI_ALLOCATED; 789 if (cur != root) 790 cur->parent = parent; 791 } 792 793 /* detect hardlink */ 794 if (cur->inode->flags & FI_WRITTEN) { 795 assert(!S_ISDIR(cur->type)); 796 hardlink = 1; 797 } else { 798 hardlink = 0; 799 } 800 cur->inode->flags |= FI_WRITTEN; 801 802 /* make sure it doesn't exist yet */ 803 vp = NULL; 804 error = hammer2_nresolve(dvp, &vp, cur->name, 805 strlen(cur->name)); 806 if (!error) 807 errx(1, "hammer2_nresolve(\"%s\") already exists", 808 cur->name); 809 hammer2_print(dvp, vp, cur, depth, "nresolve"); 810 811 /* if directory, mkdir and recurse */ 812 if (S_ISDIR(cur->type)) { 813 assert(cur->child); 814 815 vp = NULL; 816 error = hammer2_nmkdir(dvp, &vp, cur->name, 817 strlen(cur->name), cur->inode->st.st_mode); 818 if (error) 819 errx(1, "hammer2_nmkdir(\"%s\") failed: %s", 820 cur->name, strerror(error)); 821 assert(vp); 822 hammer2_print(dvp, vp, cur, depth, "nmkdir"); 823 824 error = hammer2_populate_dir(vp, path, cur->child, cur, 825 fsopts, depth + 1); 826 if (error) 827 errx(1, "failed to populate %s: %s", 828 path, strerror(error)); 829 cur->inode->param = vp; 830 continue; 831 } 832 833 /* if regular file, creat and write its data */ 834 if (S_ISREG(cur->type) && !hardlink) { 835 assert(cur->child == NULL); 836 837 vp = NULL; 838 error = hammer2_ncreate(dvp, &vp, cur->name, 839 strlen(cur->name), cur->inode->st.st_mode); 840 if (error) 841 errx(1, "hammer2_ncreate(\"%s\") failed: %s", 842 cur->name, strerror(error)); 843 assert(vp); 844 hammer2_print(dvp, vp, cur, depth, "ncreate"); 845 846 error = hammer2_write_file(vp, path, cur); 847 if (error) 848 errx(1, "hammer2_write_file(\"%s\") failed: %s", 849 path, strerror(error)); 850 cur->inode->param = vp; 851 continue; 852 } 853 854 /* if symlink, create a symlink against target */ 855 if (S_ISLNK(cur->type)) { 856 assert(cur->child == NULL); 857 858 vp = NULL; 859 error = hammer2_nsymlink(dvp, &vp, cur->name, 860 strlen(cur->name), cur->symlink, 861 cur->inode->st.st_mode); 862 if (error) 863 errx(1, "hammer2_nsymlink(\"%s\") failed: %s", 864 cur->name, strerror(error)); 865 assert(vp); 866 hammer2_print(dvp, vp, cur, depth, "nsymlink"); 867 cur->inode->param = vp; 868 continue; 869 } 870 871 /* if fifo, create a fifo */ 872 if (S_ISFIFO(cur->type) && !hardlink) { 873 assert(cur->child == NULL); 874 875 vp = NULL; 876 error = hammer2_nmknod(dvp, &vp, cur->name, 877 strlen(cur->name), VFIFO, cur->inode->st.st_mode); 878 if (error) 879 errx(1, "hammer2_nmknod(\"%s\") failed: %s", 880 cur->name, strerror(error)); 881 assert(vp); 882 hammer2_print(dvp, vp, cur, depth, "nmknod"); 883 cur->inode->param = vp; 884 continue; 885 } 886 887 /* if hardlink, creat a hardlink */ 888 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) { 889 char buf[64]; 890 assert(cur->child == NULL); 891 892 /* source vnode must not be NULL */ 893 vp = cur->inode->param; 894 assert(vp); 895 /* currently these conditions must be true */ 896 assert(vp->v_data); 897 assert(vp->v_type == VREG || vp->v_type == VFIFO); 898 assert(vp->v_logical); 899 assert(!vp->v_vflushed); 900 assert(vp->v_malloced); 901 assert(VTOI(vp)->refs > 0); 902 903 error = hammer2_nlink(dvp, vp, cur->name, 904 strlen(cur->name)); 905 if (error) 906 errx(1, "hammer2_nlink(\"%s\") failed: %s", 907 cur->name, strerror(error)); 908 snprintf(buf, sizeof(buf), "nlink=%lld", 909 (long long)VTOI(vp)->meta.nlinks); 910 hammer2_print(dvp, vp, cur, depth, buf); 911 continue; 912 } 913 914 /* other types are unsupported */ 915 printf("ignore %s 0%o\n", path, cur->type); 916 } 917 918 return 0; 919 } 920 921 static int 922 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node) 923 { 924 struct stat *st = &node->inode->st; 925 size_t nsize, bufsize; 926 off_t offset; 927 int fd, error; 928 char *p; 929 930 nsize = st->st_size; 931 if (nsize == 0) 932 return 0; 933 /* check nsize vs maximum file size */ 934 935 fd = open(path, O_RDONLY); 936 if (fd < 0) 937 err(1, "failed to open %s", path); 938 939 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 940 if (p == MAP_FAILED) 941 err(1, "failed to mmap %s", path); 942 close(fd); 943 944 for (offset = 0; offset < nsize; ) { 945 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE); 946 assert(bufsize <= HAMMER2_PBUFSIZE); 947 error = hammer2_write(vp, p + offset, bufsize, offset); 948 if (error) 949 errx(1, "failed to write to %s vnode: %s", 950 path, strerror(error)); 951 offset += bufsize; 952 if (bufsize == HAMMER2_PBUFSIZE) 953 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0); 954 } 955 munmap(p, nsize); 956 957 return 0; 958 } 959 960 static int 961 hammer2_bulkfree(struct m_vnode *vp) 962 { 963 hammer2_ioc_bulkfree_t bfi; 964 size_t usermem; 965 size_t usermem_size = sizeof(usermem); 966 967 bzero(&bfi, sizeof(bfi)); 968 usermem = 0; 969 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0) 970 bfi.size = usermem / 16; 971 else 972 bfi.size = 0; 973 if (bfi.size < 8192 * 1024) 974 bfi.size = 8192 * 1024; 975 976 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi); 977 } 978 979 static int 980 hammer2_destroy_path(struct m_vnode *vp, const char *f) 981 { 982 hammer2_ioc_destroy_t destroy; 983 int error = 0; 984 985 bzero(&destroy, sizeof(destroy)); 986 destroy.cmd = HAMMER2_DELETE_FILE; 987 snprintf(destroy.path, sizeof(destroy.path), "%s", f); 988 989 printf("%s\t", f); 990 fflush(stdout); 991 992 error = hammer2_ioctl_destroy(VTOI(vp), &destroy); 993 if (error) 994 printf("%s\n", strerror(error)); 995 else 996 printf("ok\n"); 997 998 return error; 999 } 1000 1001 static int 1002 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum) 1003 { 1004 hammer2_ioc_destroy_t destroy; 1005 int error = 0; 1006 1007 bzero(&destroy, sizeof(destroy)); 1008 destroy.cmd = HAMMER2_DELETE_INUM; 1009 destroy.inum = inum; 1010 1011 printf("%jd\t", (intmax_t)destroy.inum); 1012 fflush(stdout); 1013 1014 error = hammer2_ioctl_destroy(VTOI(vp), &destroy); 1015 if (error) 1016 printf("%s\n", strerror(error)); 1017 else 1018 printf("ok\n"); 1019 1020 return error; 1021 } 1022 1023 static int 1024 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size) 1025 { 1026 hammer2_ioc_growfs_t growfs; 1027 int error; 1028 1029 bzero(&growfs, sizeof(growfs)); 1030 growfs.size = size; 1031 1032 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL); 1033 if (!error) { 1034 if (growfs.modified) 1035 printf("grown to %016jx\n", (intmax_t)growfs.size); 1036 else 1037 printf("no size change - %016jx\n", 1038 (intmax_t)growfs.size); 1039 } 1040 1041 return error; 1042 } 1043