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