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