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 #include <sys/time.h> 44 #include <sys/dirent.h> 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <stdbool.h> 49 #include <string.h> 50 #include <ctype.h> 51 #include <unistd.h> 52 #include <fcntl.h> 53 #include <time.h> 54 #include <err.h> 55 #include <assert.h> 56 #include <util.h> 57 58 #include "makefs.h" 59 #include "hammer2.h" 60 61 #define APRINTF(X, ...) \ 62 printf("%s: " X, __func__, ## __VA_ARGS__) 63 64 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *); 65 static void hammer2_parse_inode_opts(const char *, fsinfo_t *); 66 static void hammer2_dump_fsinfo(fsinfo_t *); 67 static int hammer2_create_image(const char *, fsinfo_t *); 68 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *, 69 fsnode *, fsinfo_t *, int); 70 static void hammer2_validate(const char *, fsnode *, fsinfo_t *); 71 static void hammer2_size_dir(fsnode *, fsinfo_t *); 72 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *); 73 static int hammer2_version_get(struct m_vnode *); 74 static int hammer2_pfs_get(struct m_vnode *); 75 static int hammer2_pfs_lookup(struct m_vnode *, const char *); 76 static int hammer2_pfs_create(struct m_vnode *, const char *); 77 static int hammer2_pfs_delete(struct m_vnode *, const char *); 78 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *); 79 static int hammer2_inode_getx(struct m_vnode *, const char *); 80 static int hammer2_inode_setcheck(struct m_vnode *, const char *); 81 static int hammer2_inode_setcomp(struct m_vnode *, const char *); 82 static int hammer2_bulkfree(struct m_vnode *); 83 static int hammer2_destroy_path(struct m_vnode *, const char *); 84 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t); 85 static int hammer2_growfs(struct m_vnode *, hammer2_off_t); 86 struct hammer2_linkq; 87 static int hammer2_readx_handle(struct m_vnode *, const char *, const char *, 88 struct hammer2_linkq *); 89 static int hammer2_readx(struct m_vnode *, const char *, const char *); 90 static void unittest_trim_slash(void); 91 92 fsnode *hammer2_curnode; 93 94 void 95 hammer2_prep_opts(fsinfo_t *fsopts) 96 { 97 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt)); 98 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 99 100 const option_t hammer2_options[] = { 101 /* newfs_hammer2(8) compatible options */ 102 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" }, 103 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" }, 104 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" }, 105 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" }, 106 /* makefs(8) specific options */ 107 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" }, 108 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32, 109 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" }, 110 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" }, 111 { 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0, 112 "emergency mode" }, 113 { 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" }, 114 { 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" }, 115 { 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" }, 116 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" }, 117 { 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" }, 118 { 'R', "Read", NULL, OPT_STRBUF, 0, 0, "offline read" }, 119 { .name = NULL }, 120 }; 121 122 hammer2_mkfs_init(opt); 123 124 /* make this tunable ? */ 125 assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT); 126 assert(opt->CheckType == HAMMER2_CHECK_XXHASH64); 127 128 /* force debug mode for mkfs */ 129 opt->DebugOpt = 1; 130 131 fsopts->fs_specific = h2_opt; 132 fsopts->fs_options = copy_opts(hammer2_options); 133 fsopts->sectorsize = DEV_BSIZE; 134 } 135 136 void 137 hammer2_cleanup_opts(fsinfo_t *fsopts) 138 { 139 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 140 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 141 142 hammer2_mkfs_cleanup(opt); 143 144 free(h2_opt); 145 free(fsopts->fs_options); 146 } 147 148 int 149 hammer2_parse_opts(const char *option, fsinfo_t *fsopts) 150 { 151 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 152 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 153 154 option_t *hammer2_options = fsopts->fs_options; 155 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */ 156 int i; 157 158 assert(option != NULL); 159 assert(fsopts != NULL); 160 161 if (debug & DEBUG_FS_PARSE_OPTS) 162 APRINTF("got `%s'\n", option); 163 164 i = set_option(hammer2_options, option, buf, sizeof(buf)); 165 if (i == -1) 166 return 0; 167 168 if (hammer2_options[i].name == NULL) 169 abort(); 170 171 switch (hammer2_options[i].letter) { 172 case 'b': 173 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 174 HAMMER2_BOOT_MAX_BYTES, 2); 175 break; 176 case 'r': 177 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 178 HAMMER2_AUX_MAX_BYTES, 2); 179 break; 180 case 'V': 181 if (strlen(buf) == 0) { 182 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET; 183 } else { 184 opt->Hammer2Version = strtol(buf, NULL, 0); 185 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN || 186 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP) 187 errx(1, "I don't understand how to format " 188 "HAMMER2 version %d", 189 opt->Hammer2Version); 190 } 191 break; 192 case 'L': 193 h2_opt->label_specified = 1; 194 if (strcasecmp(buf, "none") == 0) 195 break; 196 if (opt->NLabels >= MAXLABELS) 197 errx(1, "Limit of %d local labels", MAXLABELS - 1); 198 if (strlen(buf) == 0) 199 errx(1, "Volume label '%s' cannot be 0-length", buf); 200 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 201 errx(1, "Volume label '%s' is too long (%d chars max)", 202 buf, HAMMER2_INODE_MAXNAME - 1); 203 opt->Label[opt->NLabels++] = strdup(buf); 204 break; 205 case 'm': 206 if (strlen(buf) == 0) 207 errx(1, "Volume label '%s' cannot be 0-length", buf); 208 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 209 errx(1, "Volume label '%s' is too long (%d chars max)", 210 buf, HAMMER2_INODE_MAXNAME - 1); 211 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label)); 212 break; 213 case 'd': 214 hammer2_debug = strtoll(buf, NULL, 0); 215 break; 216 case 'P': 217 if (strlen(buf) == 0) 218 errx(1, "PFS argument '%s' cannot be 0-length", buf); 219 hammer2_parse_pfs_opts(buf, fsopts); 220 break; 221 case 'I': 222 if (strlen(buf) == 0) 223 errx(1, "Inode argument '%s' cannot be 0-length", buf); 224 hammer2_parse_inode_opts(buf, fsopts); 225 break; 226 case 'B': 227 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN; 228 break; 229 case 'D': 230 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY; 231 if (strlen(buf) == 0) 232 errx(1, "Destroy argument '%s' cannot be 0-length", buf); 233 if (buf[0] == '/') { 234 strlcpy(h2_opt->destroy_path, buf, 235 sizeof(h2_opt->destroy_path)); 236 } else if (strncmp(buf, "0x", 2) == 0 || 237 (buf[0] >= '0' && buf[0] <= '9')) { 238 h2_opt->destroy_inum = strtoull(buf, NULL, 0); 239 if (errno) 240 err(1, "strtoull"); 241 } else { 242 errx(1, "Invalid destroy argument %s", buf); 243 } 244 break; 245 case 'G': 246 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS; 247 break; 248 case 'R': 249 h2_opt->ioctl_cmd = HAMMER2IOC_READ; 250 if (strlen(buf) == 0) 251 errx(1, "Read argument '%s' cannot be 0-length", buf); 252 strlcpy(h2_opt->read_path, buf, sizeof(h2_opt->read_path)); 253 break; 254 default: 255 break; 256 } 257 258 if (hammer2_debug && h2_opt->ioctl_cmd) 259 unittest_trim_slash(); 260 261 return 1; 262 } 263 264 void 265 hammer2_makefs(const char *image, const char *dir, fsnode *root, 266 fsinfo_t *fsopts) 267 { 268 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 269 struct mount mp; 270 struct hammer2_mount_info info; 271 struct m_vnode devvp, *vroot; 272 hammer2_inode_t *iroot; 273 struct timeval start; 274 int error; 275 276 /* ioctl commands could have NULL dir / root */ 277 assert(image != NULL); 278 assert(fsopts != NULL); 279 280 if (debug & DEBUG_FS_MAKEFS) 281 APRINTF("image \"%s\" directory \"%s\" root %p\n", 282 image, dir, root); 283 284 /* validate tree and options */ 285 TIMER_START(start); 286 hammer2_validate(dir, root, fsopts); 287 TIMER_RESULTS(start, "hammer2_validate"); 288 289 if (h2_opt->ioctl_cmd) { 290 /* open existing image */ 291 fsopts->fd = open(image, O_RDWR); 292 if (fsopts->fd < 0) 293 err(1, "failed to open `%s'", image); 294 } else { 295 /* create image */ 296 TIMER_START(start); 297 if (hammer2_create_image(image, fsopts) == -1) 298 errx(1, "image file `%s' not created", image); 299 TIMER_RESULTS(start, "hammer2_create_image"); 300 } 301 assert(fsopts->fd > 0); 302 303 if (debug & DEBUG_FS_MAKEFS) 304 putchar('\n'); 305 306 /* vfs init */ 307 error = hammer2_vfs_init(); 308 if (error) 309 errx(1, "failed to vfs init, error %d", error); 310 311 /* mount image */ 312 memset(&devvp, 0, sizeof(devvp)); 313 devvp.fs = fsopts; 314 memset(&mp, 0, sizeof(mp)); 315 memset(&info, 0, sizeof(info)); 316 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info); 317 if (error) 318 errx(1, "failed to mount, error %d", error); 319 assert(mp.mnt_data); 320 321 /* get root vnode */ 322 vroot = NULL; 323 error = hammer2_vfs_root(&mp, &vroot); 324 if (error) 325 errx(1, "failed to get root vnode, error %d", error); 326 assert(vroot); 327 328 iroot = VTOI(vroot); 329 assert(iroot); 330 printf("root inode inum %lld, mode 0%o, refs %d\n", 331 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs); 332 333 if (h2_opt->emergency_mode) 334 hammer2_ioctl_emerg_mode(iroot, 1); 335 336 switch (h2_opt->ioctl_cmd) { 337 case HAMMER2IOC_VERSION_GET: 338 printf("version get `%s'\n", image); 339 TIMER_START(start); 340 error = hammer2_version_get(vroot); 341 if (error) 342 errx(1, "version get `%s' failed '%s'", image, 343 strerror(error)); 344 TIMER_RESULTS(start, "hammer2_version_get"); 345 break; 346 case HAMMER2IOC_PFS_GET: 347 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 348 TIMER_START(start); 349 error = hammer2_pfs_get(vroot); 350 if (error) 351 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 352 image, strerror(error)); 353 TIMER_RESULTS(start, "hammer2_pfs_get"); 354 break; 355 case HAMMER2IOC_PFS_LOOKUP: 356 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 357 TIMER_START(start); 358 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name); 359 if (error) 360 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 361 image, strerror(error)); 362 TIMER_RESULTS(start, "hammer2_pfs_lookup"); 363 break; 364 case HAMMER2IOC_PFS_CREATE: 365 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 366 TIMER_START(start); 367 error = hammer2_pfs_create(vroot, h2_opt->pfs_name); 368 if (error) 369 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 370 image, strerror(error)); 371 TIMER_RESULTS(start, "hammer2_pfs_create"); 372 break; 373 case HAMMER2IOC_PFS_DELETE: 374 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 375 TIMER_START(start); 376 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name); 377 if (error) 378 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 379 image, strerror(error)); 380 TIMER_RESULTS(start, "hammer2_pfs_delete"); 381 break; 382 case HAMMER2IOC_PFS_SNAPSHOT: 383 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 384 TIMER_START(start); 385 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name, 386 h2_opt->mount_label); 387 if (error) 388 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 389 image, strerror(error)); 390 TIMER_RESULTS(start, "hammer2_pfs_snapshot"); 391 break; 392 case HAMMER2IOC_INODE_GET: 393 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image); 394 TIMER_START(start); 395 error = hammer2_inode_getx(vroot, h2_opt->inode_path); 396 if (error) 397 errx(1, "inode %s `%s' failed '%s'", 398 h2_opt->inode_cmd_name, image, strerror(error)); 399 TIMER_RESULTS(start, "hammer2_inode_getx"); 400 break; 401 case HAMMER2IOC_INODE_SET: 402 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image); 403 TIMER_START(start); 404 if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) { 405 error = hammer2_inode_setcheck(vroot, 406 h2_opt->inode_path); 407 if (error) 408 errx(1, "inode %s `%s' failed '%s'", 409 h2_opt->inode_cmd_name, image, 410 strerror(error)); 411 } else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) { 412 error = hammer2_inode_setcomp(vroot, 413 h2_opt->inode_path); 414 if (error) 415 errx(1, "inode %s `%s' failed '%s'", 416 h2_opt->inode_cmd_name, image, 417 strerror(error)); 418 } else { 419 assert(0); 420 } 421 TIMER_RESULTS(start, "hammer2_inode_setx"); 422 break; 423 case HAMMER2IOC_BULKFREE_SCAN: 424 printf("bulkfree `%s'\n", image); 425 TIMER_START(start); 426 error = hammer2_bulkfree(vroot); 427 if (error) 428 errx(1, "bulkfree `%s' failed '%s'", image, 429 strerror(error)); 430 TIMER_RESULTS(start, "hammer2_bulkfree"); 431 break; 432 case HAMMER2IOC_DESTROY: 433 TIMER_START(start); 434 if (strlen(h2_opt->destroy_path)) { 435 printf("destroy `%s' in `%s'\n", 436 h2_opt->destroy_path, image); 437 error = hammer2_destroy_path(vroot, 438 h2_opt->destroy_path); 439 if (error) 440 errx(1, "destroy `%s' in `%s' failed '%s'", 441 h2_opt->destroy_path, image, 442 strerror(error)); 443 } else { 444 printf("destroy %lld in `%s'\n", 445 (long long)h2_opt->destroy_inum, image); 446 error = hammer2_destroy_inum(vroot, 447 h2_opt->destroy_inum); 448 if (error) 449 errx(1, "destroy %lld in `%s' failed '%s'", 450 (long long)h2_opt->destroy_inum, image, 451 strerror(error)); 452 } 453 TIMER_RESULTS(start, "hammer2_destroy"); 454 break; 455 case HAMMER2IOC_GROWFS: 456 printf("growfs `%s'\n", image); 457 TIMER_START(start); 458 error = hammer2_growfs(vroot, h2_opt->image_size); 459 if (error) 460 errx(1, "growfs `%s' failed '%s'", image, 461 strerror(error)); 462 TIMER_RESULTS(start, "hammer2_growfs"); 463 break; 464 case HAMMER2IOC_READ: 465 printf("read `%s'\n", image); 466 TIMER_START(start); 467 error = hammer2_readx(vroot, dir, h2_opt->read_path); 468 if (error) 469 errx(1, "read `%s' failed '%s'", image, 470 strerror(error)); 471 TIMER_RESULTS(start, "hammer2_readx"); 472 break; 473 default: 474 printf("populating `%s'\n", image); 475 TIMER_START(start); 476 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0)) 477 errx(1, "image file `%s' not populated", image); 478 TIMER_RESULTS(start, "hammer2_populate_dir"); 479 break; 480 } 481 482 /* unmount image */ 483 error = hammer2_vfs_unmount(&mp, 0); 484 if (error) 485 errx(1, "failed to unmount, error %d", error); 486 487 /* check leaked resource */ 488 if (vnode_count) 489 printf("XXX %lld vnode left\n", (long long)vnode_count); 490 if (hammer2_chain_allocs) 491 printf("XXX %ld chain left\n", hammer2_chain_allocs); 492 bcleanup(); 493 494 /* vfs uninit */ 495 error = hammer2_vfs_uninit(); 496 if (error) 497 errx(1, "failed to vfs uninit, error %d", error); 498 499 if (close(fsopts->fd) == -1) 500 err(1, "closing `%s'", image); 501 fsopts->fd = -1; 502 503 printf("image `%s' complete\n", image); 504 } 505 506 /* end of public functions */ 507 508 static void 509 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts) 510 { 511 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 512 char *o, *p; 513 size_t n; 514 515 o = p = strdup(buf); 516 p = strchr(p, ':'); 517 if (p != NULL) { 518 *p++ = 0; 519 n = strlen(p); 520 } else { 521 n = 0; 522 } 523 524 if (!strcmp(o, "get") || !strcmp(o, "list")) { 525 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET; 526 } else if (!strcmp(o, "lookup")) { 527 if (n == 0 || n > NAME_MAX) 528 errx(1, "invalid PFS name \"%s\"", p); 529 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP; 530 } else if (!strcmp(o, "create")) { 531 if (n == 0 || n > NAME_MAX) 532 errx(1, "invalid PFS name \"%s\"", p); 533 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE; 534 } else if (!strcmp(o, "delete")) { 535 if (n == 0 || n > NAME_MAX) 536 errx(1, "invalid PFS name \"%s\"", p); 537 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE; 538 } else if (!strcmp(o, "snapshot")) { 539 if (n > NAME_MAX) 540 errx(1, "invalid PFS name \"%s\"", p); 541 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT; 542 } else { 543 errx(1, "invalid PFS command \"%s\"", o); 544 } 545 546 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name)); 547 if (n > 0) 548 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name)); 549 550 free(o); 551 } 552 553 static void 554 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts) 555 { 556 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 557 char *o, *p; 558 size_t n; 559 560 o = p = strdup(buf); 561 p = strchr(p, ':'); 562 if (p != NULL) { 563 *p++ = 0; 564 n = strlen(p); 565 } else { 566 n = 0; 567 } 568 569 if (!strcmp(o, "get")) { 570 if (n == 0 || n > PATH_MAX) 571 errx(1, "invalid file path \"%s\"", p); 572 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET; 573 } else if (!strcmp(o, "setcheck")) { 574 if (n == 0 || n > PATH_MAX - 10) 575 errx(1, "invalid argument \"%s\"", p); 576 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET; 577 } else if (!strcmp(o, "setcomp")) { 578 if (n == 0 || n > PATH_MAX - 10) 579 errx(1, "invalid argument \"%s\"", p); 580 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET; 581 } else { 582 errx(1, "invalid inode command \"%s\"", o); 583 } 584 585 strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name)); 586 if (n > 0) 587 strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path)); 588 589 free(o); 590 } 591 592 static hammer2_off_t 593 hammer2_image_size(fsinfo_t *fsopts) 594 { 595 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 596 hammer2_off_t image_size, used_size = 0; 597 int num_level1, delta_num_level1; 598 599 /* use 4 volume headers by default */ 600 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */ 601 assert(num_level1 != 0); 602 assert(num_level1 <= 8); 603 604 /* add 4MiB segment for each level1 */ 605 used_size += HAMMER2_ZONE_SEG64 * num_level1; 606 607 /* add boot/aux area, but exact size unknown at this point */ 608 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES; 609 610 /* add data size */ 611 used_size += fsopts->size; 612 613 /* XXX add extra level1 for meta data and indirect blocks */ 614 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 615 616 /* XXX add extra level1 for safety */ 617 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10) 618 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 619 620 /* use 8GiB image size by default */ 621 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1; 622 printf("trying default image size %s\n", sizetostr(image_size)); 623 624 /* adjust if image size isn't large enough */ 625 if (used_size > image_size) { 626 /* determine extra level1 needed */ 627 delta_num_level1 = howmany(used_size - image_size, 628 HAMMER2_FREEMAP_LEVEL1_SIZE); 629 630 /* adjust used size with 4MiB segment for each extra level1 */ 631 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1; 632 633 /* adjust image size with extra level1 */ 634 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1; 635 printf("trying adjusted image size %s\n", 636 sizetostr(image_size)); 637 638 if (used_size > image_size) 639 errx(1, "invalid used_size %lld > image_size %lld", 640 (long long)used_size, (long long)image_size); 641 } 642 643 return image_size; 644 } 645 646 static const char * 647 hammer2_label_name(int label_type) 648 { 649 switch (label_type) { 650 case HAMMER2_LABEL_NONE: 651 return "NONE"; 652 case HAMMER2_LABEL_BOOT: 653 return "BOOT"; 654 case HAMMER2_LABEL_ROOT: 655 return "ROOT"; 656 case HAMMER2_LABEL_DATA: 657 return "DATA"; 658 default: 659 assert(0); 660 } 661 return NULL; 662 } 663 664 static void 665 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) 666 { 667 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 668 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 669 hammer2_off_t image_size = 0, minsize, maxsize; 670 const char *s; 671 672 /* ioctl commands could have NULL dir / root */ 673 assert(fsopts != NULL); 674 675 if (debug & DEBUG_FS_VALIDATE) { 676 APRINTF("before defaults set:\n"); 677 hammer2_dump_fsinfo(fsopts); 678 } 679 680 /* makefs only supports "DATA" for default PFS label */ 681 if (!h2_opt->label_specified) { 682 opt->DefaultLabelType = HAMMER2_LABEL_DATA; 683 s = hammer2_label_name(opt->DefaultLabelType); 684 printf("using default label \"%s\"\n", s); 685 } 686 687 /* set default mount PFS label */ 688 if (!strcmp(h2_opt->mount_label, "")) { 689 s = hammer2_label_name(HAMMER2_LABEL_DATA); 690 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label)); 691 printf("using default mount label \"%s\"\n", s); 692 } 693 694 /* set default number of volume headers */ 695 if (!h2_opt->num_volhdr) { 696 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS; 697 printf("using default %d volume headers\n", h2_opt->num_volhdr); 698 } 699 700 /* done if ioctl commands */ 701 if (h2_opt->ioctl_cmd) { 702 if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS) 703 goto ignore_size_dir; 704 else 705 goto done; 706 } 707 708 /* calculate data size */ 709 if (fsopts->size != 0) 710 fsopts->size = 0; /* shouldn't reach here to begin with */ 711 if (root == NULL) 712 errx(1, "fsnode tree not constructed"); 713 hammer2_size_dir(root, fsopts); 714 printf("estimated data size %s from %lld inode\n", 715 sizetostr(fsopts->size), (long long)fsopts->inodes); 716 717 /* determine image size from data size */ 718 image_size = hammer2_image_size(fsopts); 719 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 720 ignore_size_dir: 721 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 722 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 723 if (image_size < minsize) 724 image_size = minsize; 725 else if (maxsize > 0 && image_size > maxsize) 726 errx(1, "`%s' size of %lld is larger than the maxsize of %lld", 727 dir, (long long)image_size, (long long)maxsize); 728 729 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 730 h2_opt->image_size = image_size; 731 printf("using %s image size\n", sizetostr(h2_opt->image_size)); 732 done: 733 if (debug & DEBUG_FS_VALIDATE) { 734 APRINTF("after defaults set:\n"); 735 hammer2_dump_fsinfo(fsopts); 736 } 737 } 738 739 static void 740 hammer2_dump_fsinfo(fsinfo_t *fsopts) 741 { 742 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 743 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 744 int i; 745 char *s; 746 747 assert(fsopts != NULL); 748 749 APRINTF("fsinfo_t at %p\n", fsopts); 750 751 printf("\tinodes %lld\n", (long long)fsopts->inodes); 752 printf("\tsize %lld, minsize %lld, maxsize %lld\n", 753 (long long)fsopts->size, 754 (long long)fsopts->minsize, 755 (long long)fsopts->maxsize); 756 757 printf("\thammer2_debug 0x%x\n", hammer2_debug); 758 759 printf("\tlabel_specified %d\n", h2_opt->label_specified); 760 printf("\tmount_label \"%s\"\n", h2_opt->mount_label); 761 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr); 762 printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd); 763 printf("\temergency_mode %d\n", h2_opt->emergency_mode); 764 printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name); 765 printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name); 766 printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name); 767 printf("\tinode_path \"%s\"\n", h2_opt->inode_path); 768 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path); 769 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum); 770 printf("\tread_path \"%s\"\n", h2_opt->read_path); 771 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size); 772 773 printf("\tHammer2Version %d\n", opt->Hammer2Version); 774 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize); 775 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize); 776 printf("\tNLabels %d\n", opt->NLabels); 777 printf("\tCompType %d\n", opt->CompType); 778 printf("\tCheckType %d\n", opt->CheckType); 779 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType); 780 printf("\tDebugOpt %d\n", opt->DebugOpt); 781 782 s = NULL; 783 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s); 784 printf("\tHammer2_FSType \"%s\"\n", s); 785 s = NULL; 786 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s); 787 printf("\tHammer2_VolFSID \"%s\"\n", s); 788 s = NULL; 789 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s); 790 printf("\tHammer2_SupCLID \"%s\"\n", s); 791 s = NULL; 792 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s); 793 printf("\tHammer2_SupFSID \"%s\"\n", s); 794 795 for (i = 0; i < opt->NLabels; i++) { 796 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]); 797 s = NULL; 798 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s); 799 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s); 800 s = NULL; 801 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s); 802 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s); 803 } 804 805 free(s); 806 } 807 808 static int 809 hammer2_create_image(const char *image, fsinfo_t *fsopts) 810 { 811 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 812 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 813 char *av[] = { (char *)image, }; /* XXX support multi-volumes */ 814 char *buf; 815 int i, bufsize, oflags; 816 off_t bufrem; 817 818 assert(image != NULL); 819 assert(fsopts != NULL); 820 821 /* create image */ 822 oflags = O_RDWR | O_CREAT; 823 if (fsopts->offset == 0) 824 oflags |= O_TRUNC; 825 if ((fsopts->fd = open(image, oflags, 0666)) == -1) { 826 warn("can't open `%s' for writing", image); 827 return -1; 828 } 829 830 /* zero image */ 831 bufsize = HAMMER2_PBUFSIZE; 832 bufrem = h2_opt->image_size; 833 if (fsopts->sparse) { 834 if (ftruncate(fsopts->fd, bufrem) == -1) { 835 warn("sparse option disabled"); 836 fsopts->sparse = 0; 837 } 838 } 839 if (fsopts->sparse) { 840 /* File truncated at bufrem. Remaining is 0 */ 841 bufrem = 0; 842 buf = NULL; 843 } else { 844 if (debug & DEBUG_FS_CREATE_IMAGE) 845 APRINTF("zero-ing image `%s', %lld sectors, " 846 "using %d byte chunks\n", 847 image, (long long)bufrem, bufsize); 848 buf = ecalloc(1, bufsize); 849 } 850 851 if (fsopts->offset != 0) { 852 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) { 853 warn("can't seek"); 854 free(buf); 855 return -1; 856 } 857 } 858 859 while (bufrem > 0) { 860 i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); 861 if (i == -1) { 862 warn("zeroing image, %lld bytes to go", 863 (long long)bufrem); 864 free(buf); 865 return -1; 866 } 867 bufrem -= i; 868 } 869 if (buf) 870 free(buf); 871 872 /* make the file system */ 873 if (debug & DEBUG_FS_CREATE_IMAGE) 874 APRINTF("calling mkfs(\"%s\", ...)\n", image); 875 hammer2_mkfs(1, av, opt); /* success if returned */ 876 877 return fsopts->fd; 878 } 879 880 static off_t 881 hammer2_phys_size(off_t size) 882 { 883 off_t radix_size, phys_size = 0; 884 int i; 885 886 if (size > HAMMER2_PBUFSIZE) { 887 phys_size += rounddown(size, HAMMER2_PBUFSIZE); 888 size = size % HAMMER2_PBUFSIZE; 889 } 890 891 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) { 892 radix_size = 1UL << i; 893 if (radix_size >= size) { 894 phys_size += radix_size; 895 break; 896 } 897 } 898 899 return phys_size; 900 } 901 902 /* calculate data size */ 903 static void 904 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts) 905 { 906 fsnode *node; 907 908 assert(fsopts != NULL); 909 910 if (debug & DEBUG_FS_SIZE_DIR) 911 APRINTF("entry: bytes %lld inodes %lld\n", 912 (long long)fsopts->size, (long long)fsopts->inodes); 913 914 for (node = root; node != NULL; node = node->next) { 915 if (node == root) { /* we're at "." */ 916 assert(strcmp(node->name, ".") == 0); 917 } else if ((node->inode->flags & FI_SIZED) == 0) { 918 /* don't count duplicate names */ 919 node->inode->flags |= FI_SIZED; 920 if (debug & DEBUG_FS_SIZE_DIR_NODE) 921 APRINTF("`%s' size %lld\n", 922 node->name, 923 (long long)node->inode->st.st_size); 924 fsopts->inodes++; 925 fsopts->size += sizeof(hammer2_inode_data_t); 926 if (node->type == S_IFREG) { 927 size_t st_size = node->inode->st.st_size; 928 if (st_size > HAMMER2_EMBEDDED_BYTES) 929 fsopts->size += hammer2_phys_size(st_size); 930 } else if (node->type == S_IFLNK) { 931 size_t nlen = strlen(node->symlink); 932 if (nlen > HAMMER2_EMBEDDED_BYTES) 933 fsopts->size += hammer2_phys_size(nlen); 934 } 935 } 936 if (node->type == S_IFDIR) 937 hammer2_size_dir(node->child, fsopts); 938 } 939 940 if (debug & DEBUG_FS_SIZE_DIR) 941 APRINTF("exit: size %lld inodes %lld\n", 942 (long long)fsopts->size, (long long)fsopts->inodes); 943 } 944 945 static void 946 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp, 947 const fsnode *node, int depth, const char *msg) 948 { 949 if (debug & DEBUG_FS_POPULATE) { 950 if (1) { 951 int indent = depth * 2; 952 char *type; 953 if (S_ISDIR(node->type)) 954 type = "dir"; 955 else if (S_ISREG(node->type)) 956 type = "reg"; 957 else if (S_ISLNK(node->type)) 958 type = "lnk"; 959 else if (S_ISFIFO(node->type)) 960 type = "fifo"; 961 else 962 type = "???"; 963 printf("%*.*s", indent, indent, ""); 964 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n", 965 dvp, dvp ? VTOI(dvp)->refs : 0, 966 vp, vp ? VTOI(vp)->refs : 0, 967 node->name, type, msg); 968 } else { 969 char type; 970 if (S_ISDIR(node->type)) 971 type = 'd'; 972 else if (S_ISREG(node->type)) 973 type = 'r'; 974 else if (S_ISLNK(node->type)) 975 type = 'l'; 976 else if (S_ISFIFO(node->type)) 977 type = 'f'; 978 else 979 type = '?'; 980 printf("%c", type); 981 fflush(stdout); 982 } 983 } 984 } 985 986 static int 987 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root, 988 fsnode *parent, fsinfo_t *fsopts, int depth) 989 { 990 fsnode *cur; 991 struct m_vnode *vp; 992 struct stat st; 993 char f[MAXPATHLEN]; 994 const char *path; 995 int hardlink; 996 int error; 997 998 assert(dvp != NULL); 999 assert(dir != NULL); 1000 assert(root != NULL); 1001 assert(parent != NULL); 1002 assert(fsopts != NULL); 1003 1004 /* assert root directory */ 1005 assert(S_ISDIR(root->type)); 1006 assert(!strcmp(root->name, ".")); 1007 assert(!root->child); 1008 assert(!root->parent || root->parent->child == root); 1009 1010 hammer2_print(dvp, NULL, root, depth, "enter"); 1011 if (stat(dir, &st) == -1) 1012 err(1, "no such path %s", dir); 1013 if (!S_ISDIR(st.st_mode)) 1014 errx(1, "no such dir %s", dir); 1015 1016 for (cur = root->next; cur != NULL; cur = cur->next) { 1017 /* global variable for HAMMER2 vnops */ 1018 hammer2_curnode = cur; 1019 1020 /* construct source path */ 1021 if (cur->contents) { 1022 path = cur->contents; 1023 } else { 1024 if (snprintf(f, sizeof(f), "%s/%s/%s", 1025 cur->root, cur->path, cur->name) >= (int)sizeof(f)) 1026 errx(1, "path %s too long", f); 1027 path = f; 1028 } 1029 if (S_ISLNK(cur->type)) { 1030 if (lstat(path, &st) == -1) 1031 err(1, "no such symlink %s", path); 1032 } else { 1033 if (stat(path, &st) == -1) 1034 err(1, "no such path %s", path); 1035 } 1036 1037 /* update node state */ 1038 if ((cur->inode->flags & FI_ALLOCATED) == 0) { 1039 cur->inode->flags |= FI_ALLOCATED; 1040 if (cur != root) 1041 cur->parent = parent; 1042 } 1043 1044 /* detect hardlink */ 1045 if (cur->inode->flags & FI_WRITTEN) { 1046 assert(!S_ISDIR(cur->type)); 1047 hardlink = 1; 1048 } else { 1049 hardlink = 0; 1050 } 1051 cur->inode->flags |= FI_WRITTEN; 1052 1053 /* make sure it doesn't exist yet */ 1054 vp = NULL; 1055 error = hammer2_nresolve(dvp, &vp, cur->name, 1056 strlen(cur->name)); 1057 if (!error) 1058 errx(1, "hammer2_nresolve(\"%s\") already exists", 1059 cur->name); 1060 hammer2_print(dvp, vp, cur, depth, "nresolve"); 1061 1062 /* if directory, mkdir and recurse */ 1063 if (S_ISDIR(cur->type)) { 1064 assert(cur->child); 1065 1066 vp = NULL; 1067 error = hammer2_nmkdir(dvp, &vp, cur->name, 1068 strlen(cur->name), cur->inode->st.st_mode); 1069 if (error) 1070 errx(1, "hammer2_nmkdir(\"%s\") failed: %s", 1071 cur->name, strerror(error)); 1072 assert(vp); 1073 hammer2_print(dvp, vp, cur, depth, "nmkdir"); 1074 1075 error = hammer2_populate_dir(vp, path, cur->child, cur, 1076 fsopts, depth + 1); 1077 if (error) 1078 errx(1, "failed to populate %s: %s", 1079 path, strerror(error)); 1080 cur->inode->param = vp; 1081 continue; 1082 } 1083 1084 /* if regular file, creat and write its data */ 1085 if (S_ISREG(cur->type) && !hardlink) { 1086 assert(cur->child == NULL); 1087 1088 vp = NULL; 1089 error = hammer2_ncreate(dvp, &vp, cur->name, 1090 strlen(cur->name), cur->inode->st.st_mode); 1091 if (error) 1092 errx(1, "hammer2_ncreate(\"%s\") failed: %s", 1093 cur->name, strerror(error)); 1094 assert(vp); 1095 hammer2_print(dvp, vp, cur, depth, "ncreate"); 1096 1097 error = hammer2_write_file(vp, path, cur); 1098 if (error) 1099 errx(1, "hammer2_write_file(\"%s\") failed: %s", 1100 path, strerror(error)); 1101 cur->inode->param = vp; 1102 continue; 1103 } 1104 1105 /* if symlink, create a symlink against target */ 1106 if (S_ISLNK(cur->type)) { 1107 assert(cur->child == NULL); 1108 1109 vp = NULL; 1110 error = hammer2_nsymlink(dvp, &vp, cur->name, 1111 strlen(cur->name), cur->symlink, 1112 cur->inode->st.st_mode); 1113 if (error) 1114 errx(1, "hammer2_nsymlink(\"%s\") failed: %s", 1115 cur->name, strerror(error)); 1116 assert(vp); 1117 hammer2_print(dvp, vp, cur, depth, "nsymlink"); 1118 cur->inode->param = vp; 1119 continue; 1120 } 1121 1122 /* if fifo, create a fifo */ 1123 if (S_ISFIFO(cur->type) && !hardlink) { 1124 assert(cur->child == NULL); 1125 1126 vp = NULL; 1127 error = hammer2_nmknod(dvp, &vp, cur->name, 1128 strlen(cur->name), VFIFO, cur->inode->st.st_mode); 1129 if (error) 1130 errx(1, "hammer2_nmknod(\"%s\") failed: %s", 1131 cur->name, strerror(error)); 1132 assert(vp); 1133 hammer2_print(dvp, vp, cur, depth, "nmknod"); 1134 cur->inode->param = vp; 1135 continue; 1136 } 1137 1138 /* if hardlink, creat a hardlink */ 1139 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) { 1140 char buf[64]; 1141 assert(cur->child == NULL); 1142 1143 /* source vnode must not be NULL */ 1144 vp = cur->inode->param; 1145 assert(vp); 1146 /* currently these conditions must be true */ 1147 assert(vp->v_data); 1148 assert(vp->v_type == VREG || vp->v_type == VFIFO); 1149 assert(vp->v_logical); 1150 assert(!vp->v_vflushed); 1151 assert(vp->v_malloced); 1152 assert(VTOI(vp)->refs > 0); 1153 1154 error = hammer2_nlink(dvp, vp, cur->name, 1155 strlen(cur->name)); 1156 if (error) 1157 errx(1, "hammer2_nlink(\"%s\") failed: %s", 1158 cur->name, strerror(error)); 1159 snprintf(buf, sizeof(buf), "nlink=%lld", 1160 (long long)VTOI(vp)->meta.nlinks); 1161 hammer2_print(dvp, vp, cur, depth, buf); 1162 continue; 1163 } 1164 1165 /* other types are unsupported */ 1166 printf("ignore %s 0%o\n", path, cur->type); 1167 } 1168 1169 return 0; 1170 } 1171 1172 static int 1173 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node) 1174 { 1175 struct stat *st = &node->inode->st; 1176 size_t nsize, bufsize; 1177 off_t offset; 1178 int fd, error; 1179 char *p; 1180 1181 nsize = st->st_size; 1182 if (nsize == 0) 1183 return 0; 1184 /* check nsize vs maximum file size */ 1185 1186 fd = open(path, O_RDONLY); 1187 if (fd < 0) 1188 err(1, "failed to open %s", path); 1189 1190 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 1191 if (p == MAP_FAILED) 1192 err(1, "failed to mmap %s", path); 1193 close(fd); 1194 1195 for (offset = 0; offset < nsize; ) { 1196 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE); 1197 assert(bufsize <= HAMMER2_PBUFSIZE); 1198 error = hammer2_write(vp, p + offset, bufsize, offset); 1199 if (error) 1200 errx(1, "failed to write to %s vnode: %s", 1201 path, strerror(error)); 1202 offset += bufsize; 1203 if (bufsize == HAMMER2_PBUFSIZE) 1204 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0); 1205 } 1206 munmap(p, nsize); 1207 1208 return 0; 1209 } 1210 1211 static int 1212 trim_char(char *p, char c) 1213 { 1214 char *o, tmp[PATH_MAX]; 1215 bool prev_was_c; 1216 size_t n; 1217 int i; 1218 1219 strlcpy(tmp, p, sizeof(tmp)); 1220 if (strncmp(tmp, p, sizeof(tmp))) 1221 return ENOSPC; 1222 1223 /* trim consecutive */ 1224 prev_was_c = false; 1225 o = p; 1226 n = strlen(p); 1227 1228 for (i = 0; i < n; i++) { 1229 if (tmp[i] == c) { 1230 if (!prev_was_c) 1231 *p++ = tmp[i]; 1232 prev_was_c = true; 1233 } else { 1234 *p++ = tmp[i]; 1235 prev_was_c = false; 1236 } 1237 } 1238 *p = 0; 1239 assert(strlen(p) <= strlen(tmp)); 1240 1241 /* assert no consecutive */ 1242 prev_was_c = false; 1243 p = o; 1244 n = strlen(p); 1245 1246 for (i = 0; i < n; i++) { 1247 if (p[i] == c) { 1248 assert(!prev_was_c); 1249 prev_was_c = true; 1250 } else { 1251 prev_was_c = false; 1252 } 1253 } 1254 1255 /* trim leading */ 1256 if (*p == c) 1257 memmove(p, p + 1, strlen(p + 1) + 1); 1258 assert(*p != '/'); 1259 1260 /* trim trailing */ 1261 p += strlen(p); 1262 p--; 1263 if (*p == c) 1264 *p = 0; 1265 assert(p[strlen(p) - 1] != '/'); 1266 1267 return 0; 1268 } 1269 1270 static int 1271 trim_slash(char *p) 1272 { 1273 return trim_char(p, '/'); 1274 } 1275 1276 static bool 1277 is_supported_link(const char *s) 1278 { 1279 /* absolute path can't be supported */ 1280 if (strlen(s) >= 1 && strncmp(s, "/", 1) == 0) 1281 return false; 1282 1283 /* XXX ".." is currently unsupported */ 1284 if (strlen(s) >= 3 && strncmp(s, "../", 3) == 0) 1285 return false; 1286 1287 return true; 1288 } 1289 1290 static int 1291 hammer2_version_get(struct m_vnode *vp) 1292 { 1293 hammer2_dev_t *hmp; 1294 1295 hmp = VTOI(vp)->pmp->pfs_hmps[0]; 1296 if (hmp == NULL) 1297 return EINVAL; 1298 1299 printf("version: %d\n", hmp->voldata.version); 1300 1301 return 0; 1302 } 1303 1304 struct pfs_entry { 1305 TAILQ_ENTRY(pfs_entry) entry; 1306 char name[NAME_MAX+1]; 1307 char s[NAME_MAX+1]; 1308 }; 1309 1310 static int 1311 hammer2_pfs_get(struct m_vnode *vp) 1312 { 1313 hammer2_ioc_pfs_t pfs; 1314 TAILQ_HEAD(, pfs_entry) head; 1315 struct pfs_entry *p, *e; 1316 char *pfs_id_str; 1317 const char *type_str; 1318 int error; 1319 1320 bzero(&pfs, sizeof(pfs)); 1321 TAILQ_INIT(&head); 1322 1323 while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) { 1324 error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs); 1325 if (error) 1326 return error; 1327 1328 pfs_id_str = NULL; 1329 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str); 1330 1331 if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) { 1332 if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE) 1333 type_str = "MASTER"; 1334 else 1335 type_str = hammer2_pfssubtype_to_str( 1336 pfs.pfs_subtype); 1337 } else { 1338 type_str = hammer2_pfstype_to_str(pfs.pfs_type); 1339 } 1340 e = ecalloc(1, sizeof(*e)); 1341 snprintf(e->name, sizeof(e->name), "%s", pfs.name); 1342 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str); 1343 free(pfs_id_str); 1344 1345 p = TAILQ_FIRST(&head); 1346 while (p) { 1347 if (strcmp(e->name, p->name) <= 0) { 1348 TAILQ_INSERT_BEFORE(p, e, entry); 1349 break; 1350 } 1351 p = TAILQ_NEXT(p, entry); 1352 } 1353 if (!p) 1354 TAILQ_INSERT_TAIL(&head, e, entry); 1355 } 1356 1357 printf("Type " 1358 "ClusterId (pfs_clid) " 1359 "Label\n"); 1360 while ((p = TAILQ_FIRST(&head)) != NULL) { 1361 printf("%s %s\n", p->s, p->name); 1362 TAILQ_REMOVE(&head, p, entry); 1363 free(p); 1364 } 1365 1366 return 0; 1367 } 1368 1369 static int 1370 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name) 1371 { 1372 hammer2_ioc_pfs_t pfs; 1373 char *pfs_id_str; 1374 int error; 1375 1376 bzero(&pfs, sizeof(pfs)); 1377 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1378 1379 error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs); 1380 if (error == 0) { 1381 printf("name: %s\n", pfs.name); 1382 printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type)); 1383 printf("subtype: %s\n", 1384 hammer2_pfssubtype_to_str(pfs.pfs_subtype)); 1385 1386 pfs_id_str = NULL; 1387 hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str); 1388 printf("fsid: %s\n", pfs_id_str); 1389 free(pfs_id_str); 1390 1391 pfs_id_str = NULL; 1392 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str); 1393 printf("clid: %s\n", pfs_id_str); 1394 free(pfs_id_str); 1395 } 1396 1397 return error; 1398 } 1399 1400 static int 1401 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name) 1402 { 1403 hammer2_ioc_pfs_t pfs; 1404 int error; 1405 1406 bzero(&pfs, sizeof(pfs)); 1407 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1408 pfs.pfs_type = HAMMER2_PFSTYPE_MASTER; 1409 uuid_create(&pfs.pfs_clid, NULL); 1410 uuid_create(&pfs.pfs_fsid, NULL); 1411 1412 error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs); 1413 if (error == EEXIST) 1414 fprintf(stderr, 1415 "NOTE: Typically the same name is " 1416 "used for cluster elements on " 1417 "different mounts,\n" 1418 " but cluster elements on the " 1419 "same mount require unique names.\n" 1420 "hammer2: pfs_create(%s): already present\n", 1421 pfs_name); 1422 1423 return error; 1424 } 1425 1426 static int 1427 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name) 1428 { 1429 hammer2_ioc_pfs_t pfs; 1430 1431 bzero(&pfs, sizeof(pfs)); 1432 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1433 1434 return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs); 1435 } 1436 1437 static int 1438 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name, 1439 const char *mount_label) 1440 { 1441 hammer2_ioc_pfs_t pfs; 1442 struct tm *tp; 1443 time_t t; 1444 1445 bzero(&pfs, sizeof(pfs)); 1446 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1447 1448 if (strlen(pfs.name) == 0) { 1449 time(&t); 1450 tp = localtime(&t); 1451 snprintf(pfs.name, sizeof(pfs.name), 1452 "%s.%04d%02d%02d.%02d%02d%02d", 1453 mount_label, 1454 tp->tm_year + 1900, 1455 tp->tm_mon + 1, 1456 tp->tm_mday, 1457 tp->tm_hour, 1458 tp->tm_min, 1459 tp->tm_sec); 1460 } 1461 1462 return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs); 1463 } 1464 1465 static int 1466 hammer2_inode_getx(struct m_vnode *dvp, const char *f) 1467 { 1468 hammer2_ioc_inode_t inode; 1469 hammer2_inode_t *ip; 1470 hammer2_inode_meta_t *meta; 1471 struct m_vnode *vp; 1472 char *o, *p, *name, *str = NULL; 1473 char tmp[PATH_MAX]; 1474 int error; 1475 uuid_t uuid; 1476 1477 assert(strlen(f) > 0); 1478 o = p = name = strdup(f); 1479 1480 error = trim_slash(p); 1481 if (error) 1482 return error; 1483 if (strlen(p) == 0) { 1484 vp = dvp; 1485 goto start_ioctl; 1486 } 1487 1488 while ((p = strchr(p, '/')) != NULL) { 1489 *p++ = 0; /* NULL terminate name */ 1490 if (!strcmp(name, ".")) { 1491 name = p; 1492 continue; 1493 } 1494 vp = NULL; 1495 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1496 if (error) 1497 return error; 1498 1499 ip = VTOI(vp); 1500 switch (ip->meta.type) { 1501 case HAMMER2_OBJTYPE_DIRECTORY: 1502 break; 1503 case HAMMER2_OBJTYPE_SOFTLINK: 1504 bzero(tmp, sizeof(tmp)); 1505 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1506 if (error) 1507 return error; 1508 if (!is_supported_link(tmp)) 1509 return EINVAL; 1510 strlcat(tmp, "/", sizeof(tmp)); 1511 strlcat(tmp, p, sizeof(tmp)); 1512 error = trim_slash(tmp); 1513 if (error) 1514 return error; 1515 p = name = tmp; 1516 continue; 1517 default: 1518 return EINVAL; 1519 } 1520 1521 dvp = vp; 1522 name = p; 1523 } 1524 1525 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1526 if (error) 1527 return error; 1528 start_ioctl: 1529 bzero(&inode, sizeof(inode)); 1530 error = hammer2_ioctl_inode_get(VTOI(vp), &inode); 1531 if (error) 1532 return error; 1533 1534 meta = &inode.ip_data.meta; 1535 printf("--------------------\n"); 1536 printf("flags = 0x%x\n", inode.flags); 1537 printf("data_count = %ju\n", (uintmax_t)inode.data_count); 1538 printf("inode_count = %ju\n", (uintmax_t)inode.inode_count); 1539 printf("--------------------\n"); 1540 printf("version = %u\n", meta->version); 1541 printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype, 1542 hammer2_pfssubtype_to_str(meta->pfs_subtype)); 1543 printf("uflags = 0x%x\n", (unsigned int)meta->uflags); 1544 printf("rmajor = %u\n", meta->rmajor); 1545 printf("rminor = %u\n", meta->rminor); 1546 printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str)); 1547 printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str)); 1548 printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str)); 1549 printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str)); 1550 uuid = meta->uid; 1551 printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1552 uuid = meta->gid; 1553 printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1554 printf("type = %u (%s)\n", meta->type, 1555 hammer2_iptype_to_str(meta->type)); 1556 printf("op_flags = 0x%x\n", meta->op_flags); 1557 printf("cap_flags = 0x%x\n", meta->cap_flags); 1558 printf("mode = 0%o\n", meta->mode); 1559 printf("inum = 0x%jx\n", (uintmax_t)meta->inum); 1560 printf("size = %ju\n", (uintmax_t)meta->size); 1561 printf("nlinks = %ju\n", (uintmax_t)meta->nlinks); 1562 printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent); 1563 printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key); 1564 printf("name_len = %u\n", meta->name_len); 1565 printf("ncopies = %u\n", meta->ncopies); 1566 printf("comp_algo = %u\n", meta->comp_algo); 1567 printf("target_type = %u\n", meta->target_type); 1568 printf("check_algo = %u\n", meta->check_algo); 1569 printf("pfs_nmasters = %u\n", meta->pfs_nmasters); 1570 printf("pfs_type = %u (%s)\n", meta->pfs_type, 1571 hammer2_pfstype_to_str(meta->pfs_type)); 1572 printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum); 1573 uuid = meta->pfs_clid; 1574 printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1575 uuid = meta->pfs_fsid; 1576 printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1577 printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota); 1578 printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota); 1579 printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid); 1580 printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check); 1581 printf("--------------------\n"); 1582 1583 free(o); 1584 1585 return error; 1586 } 1587 1588 static int 1589 hammer2_inode_setcheck(struct m_vnode *dvp, const char *f) 1590 { 1591 hammer2_ioc_inode_t inode; 1592 hammer2_inode_t *ip; 1593 struct m_vnode *vp; 1594 char *o, *p, *name, *check_algo_str; 1595 char tmp[PATH_MAX]; 1596 const char *checks[] = { "none", "disabled", "crc32", "xxhash64", 1597 "sha192", }; 1598 int check_algo_idx, error; 1599 uint8_t check_algo; 1600 1601 assert(strlen(f) > 0); 1602 o = p = strdup(f); 1603 1604 p = strrchr(p, ':'); 1605 if (p == NULL) 1606 return EINVAL; 1607 1608 *p++ = 0; /* NULL terminate path */ 1609 check_algo_str = p; 1610 name = p = o; 1611 1612 error = trim_slash(p); 1613 if (error) 1614 return error; 1615 if (strlen(p) == 0 || strlen(check_algo_str) == 0) 1616 return EINVAL; 1617 1618 /* convert check_algo_str to check_algo_idx */ 1619 check_algo_idx = nitems(checks); 1620 while (--check_algo_idx >= 0) 1621 if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0) 1622 break; 1623 if (check_algo_idx < 0) { 1624 if (strcasecmp(check_algo_str, "default") == 0) { 1625 check_algo_str = "xxhash64"; 1626 check_algo_idx = HAMMER2_CHECK_XXHASH64; 1627 } else if (strcasecmp(check_algo_str, "disabled") == 0) { 1628 check_algo_str = "disabled"; 1629 check_algo_idx = HAMMER2_CHECK_DISABLED; 1630 } else { 1631 printf("invalid check_algo_str: %s\n", check_algo_str); 1632 return EINVAL; 1633 } 1634 } 1635 check_algo = HAMMER2_ENC_ALGO(check_algo_idx); 1636 printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str); 1637 1638 while ((p = strchr(p, '/')) != NULL) { 1639 *p++ = 0; /* NULL terminate name */ 1640 if (!strcmp(name, ".")) { 1641 name = p; 1642 continue; 1643 } 1644 vp = NULL; 1645 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1646 if (error) 1647 return error; 1648 1649 ip = VTOI(vp); 1650 switch (ip->meta.type) { 1651 case HAMMER2_OBJTYPE_DIRECTORY: 1652 break; 1653 case HAMMER2_OBJTYPE_SOFTLINK: 1654 bzero(tmp, sizeof(tmp)); 1655 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1656 if (error) 1657 return error; 1658 if (!is_supported_link(tmp)) 1659 return EINVAL; 1660 strlcat(tmp, "/", sizeof(tmp)); 1661 strlcat(tmp, p, sizeof(tmp)); 1662 error = trim_slash(tmp); 1663 if (error) 1664 return error; 1665 p = name = tmp; 1666 continue; 1667 default: 1668 return EINVAL; 1669 } 1670 1671 dvp = vp; 1672 name = p; 1673 } 1674 1675 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1676 if (error) 1677 return error; 1678 ip = VTOI(vp); 1679 1680 bzero(&inode, sizeof(inode)); 1681 error = hammer2_ioctl_inode_get(ip, &inode); 1682 if (error) 1683 return error; 1684 1685 inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK; 1686 inode.ip_data.meta.check_algo = check_algo; 1687 error = hammer2_ioctl_inode_set(ip, &inode); 1688 if (error) 1689 return error; 1690 1691 free(o); 1692 1693 return error; 1694 } 1695 1696 static int 1697 hammer2_inode_setcomp(struct m_vnode *dvp, const char *f) 1698 { 1699 hammer2_ioc_inode_t inode; 1700 hammer2_inode_t *ip; 1701 struct m_vnode *vp; 1702 char *o, *p, *name, *comp_algo_str, *comp_level_str; 1703 char tmp[PATH_MAX]; 1704 const char *comps[] = { "none", "autozero", "lz4", "zlib", }; 1705 int comp_algo_idx, comp_level_idx, error; 1706 uint8_t comp_algo, comp_level; 1707 1708 assert(strlen(f) > 0); 1709 o = p = strdup(f); 1710 1711 p = strrchr(p, ':'); 1712 if (p == NULL) 1713 return EINVAL; 1714 1715 *p++ = 0; /* NULL terminate comp_algo_str */ 1716 comp_level_str = p; 1717 p = o; 1718 1719 p = strrchr(p, ':'); 1720 if (p == NULL) { 1721 /* comp_level_str not specified */ 1722 comp_algo_str = comp_level_str; 1723 comp_level_str = NULL; 1724 } else { 1725 *p++ = 0; /* NULL terminate path */ 1726 comp_algo_str = p; 1727 } 1728 name = p = o; 1729 1730 error = trim_slash(p); 1731 if (error) 1732 return error; 1733 if (strlen(p) == 0 || strlen(comp_algo_str) == 0) 1734 return EINVAL; 1735 1736 /* convert comp_algo_str to comp_algo_idx */ 1737 comp_algo_idx = nitems(comps); 1738 while (--comp_algo_idx >= 0) 1739 if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0) 1740 break; 1741 if (comp_algo_idx < 0) { 1742 if (strcasecmp(comp_algo_str, "default") == 0) { 1743 comp_algo_str = "lz4"; 1744 comp_algo_idx = HAMMER2_COMP_LZ4; 1745 } else if (strcasecmp(comp_algo_str, "disabled") == 0) { 1746 comp_algo_str = "autozero"; 1747 comp_algo_idx = HAMMER2_COMP_AUTOZERO; 1748 } else { 1749 printf("invalid comp_algo_str: %s\n", comp_algo_str); 1750 return EINVAL; 1751 } 1752 } 1753 comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx); 1754 1755 /* convert comp_level_str to comp_level_idx */ 1756 if (comp_level_str == NULL) { 1757 comp_level_idx = 0; 1758 } else if (isdigit((int)comp_level_str[0])) { 1759 comp_level_idx = strtol(comp_level_str, NULL, 0); 1760 } else if (strcasecmp(comp_level_str, "default") == 0) { 1761 comp_level_idx = 0; 1762 } else { 1763 printf("invalid comp_level_str: %s\n", comp_level_str); 1764 return EINVAL; 1765 } 1766 if (comp_level_idx) { 1767 switch (comp_algo) { 1768 case HAMMER2_COMP_ZLIB: 1769 if (comp_level_idx < 6 || comp_level_idx > 9) { 1770 printf("unsupported comp_level %d for %s\n", 1771 comp_level_idx, comp_algo_str); 1772 return EINVAL; 1773 } 1774 break; 1775 default: 1776 printf("unsupported comp_level %d for %s\n", 1777 comp_level_idx, comp_algo_str); 1778 return EINVAL; 1779 } 1780 } 1781 comp_level = HAMMER2_ENC_LEVEL(comp_level_idx); 1782 printf("change %s to algo %d (%s) level %d\n", 1783 p, comp_algo, comp_algo_str, comp_level_idx); 1784 1785 while ((p = strchr(p, '/')) != NULL) { 1786 *p++ = 0; /* NULL terminate name */ 1787 if (!strcmp(name, ".")) { 1788 name = p; 1789 continue; 1790 } 1791 vp = NULL; 1792 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1793 if (error) 1794 return error; 1795 1796 ip = VTOI(vp); 1797 switch (ip->meta.type) { 1798 case HAMMER2_OBJTYPE_DIRECTORY: 1799 break; 1800 case HAMMER2_OBJTYPE_SOFTLINK: 1801 bzero(tmp, sizeof(tmp)); 1802 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1803 if (error) 1804 return error; 1805 if (!is_supported_link(tmp)) 1806 return EINVAL; 1807 strlcat(tmp, "/", sizeof(tmp)); 1808 strlcat(tmp, p, sizeof(tmp)); 1809 error = trim_slash(tmp); 1810 if (error) 1811 return error; 1812 p = name = tmp; 1813 continue; 1814 default: 1815 return EINVAL; 1816 } 1817 1818 dvp = vp; 1819 name = p; 1820 } 1821 1822 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1823 if (error) 1824 return error; 1825 ip = VTOI(vp); 1826 1827 bzero(&inode, sizeof(inode)); 1828 error = hammer2_ioctl_inode_get(ip, &inode); 1829 if (error) 1830 return error; 1831 1832 inode.flags |= HAMMER2IOC_INODE_FLAG_COMP; 1833 inode.ip_data.meta.comp_algo = comp_algo | comp_level; 1834 error = hammer2_ioctl_inode_set(ip, &inode); 1835 if (error) 1836 return error; 1837 1838 free(o); 1839 1840 return error; 1841 } 1842 1843 static int 1844 hammer2_bulkfree(struct m_vnode *vp) 1845 { 1846 hammer2_ioc_bulkfree_t bfi; 1847 size_t usermem; 1848 size_t usermem_size = sizeof(usermem); 1849 1850 bzero(&bfi, sizeof(bfi)); 1851 usermem = 0; 1852 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0) 1853 bfi.size = usermem / 16; 1854 else 1855 bfi.size = 0; 1856 if (bfi.size < 8192 * 1024) 1857 bfi.size = 8192 * 1024; 1858 1859 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi); 1860 } 1861 1862 static int 1863 hammer2_destroy_path(struct m_vnode *dvp, const char *f) 1864 { 1865 hammer2_ioc_destroy_t destroy; 1866 hammer2_inode_t *ip; 1867 struct m_vnode *vp; 1868 char *o, *p, *name; 1869 char tmp[PATH_MAX]; 1870 int error; 1871 1872 assert(strlen(f) > 0); 1873 o = p = name = strdup(f); 1874 1875 error = trim_slash(p); 1876 if (error) 1877 return error; 1878 if (strlen(p) == 0) 1879 return EINVAL; 1880 1881 while ((p = strchr(p, '/')) != NULL) { 1882 *p++ = 0; /* NULL terminate name */ 1883 if (!strcmp(name, ".")) { 1884 name = p; 1885 continue; 1886 } 1887 vp = NULL; 1888 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1889 if (error) 1890 return error; 1891 1892 ip = VTOI(vp); 1893 switch (ip->meta.type) { 1894 case HAMMER2_OBJTYPE_DIRECTORY: 1895 break; 1896 case HAMMER2_OBJTYPE_SOFTLINK: 1897 bzero(tmp, sizeof(tmp)); 1898 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1899 if (error) 1900 return error; 1901 if (!is_supported_link(tmp)) 1902 return EINVAL; 1903 strlcat(tmp, "/", sizeof(tmp)); 1904 strlcat(tmp, p, sizeof(tmp)); 1905 error = trim_slash(tmp); 1906 if (error) 1907 return error; 1908 p = name = tmp; 1909 continue; 1910 default: 1911 return EINVAL; 1912 } 1913 1914 dvp = vp; 1915 name = p; 1916 } 1917 1918 /* XXX When does (or why does not) ioctl modify this inode ? */ 1919 hammer2_inode_modify(VTOI(dvp)); 1920 1921 bzero(&destroy, sizeof(destroy)); 1922 destroy.cmd = HAMMER2_DELETE_FILE; 1923 snprintf(destroy.path, sizeof(destroy.path), "%s", name); 1924 1925 printf("%s\t", f); 1926 fflush(stdout); 1927 1928 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy); 1929 if (error) 1930 printf("%s\n", strerror(error)); 1931 else 1932 printf("ok\n"); 1933 free(o); 1934 1935 return error; 1936 } 1937 1938 static int 1939 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum) 1940 { 1941 hammer2_ioc_destroy_t destroy; 1942 int error; 1943 1944 bzero(&destroy, sizeof(destroy)); 1945 destroy.cmd = HAMMER2_DELETE_INUM; 1946 destroy.inum = inum; 1947 1948 printf("%jd\t", (intmax_t)destroy.inum); 1949 fflush(stdout); 1950 1951 error = hammer2_ioctl_destroy(VTOI(vp), &destroy); 1952 if (error) 1953 printf("%s\n", strerror(error)); 1954 else 1955 printf("ok\n"); 1956 1957 return error; 1958 } 1959 1960 static int 1961 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size) 1962 { 1963 hammer2_ioc_growfs_t growfs; 1964 int error; 1965 1966 bzero(&growfs, sizeof(growfs)); 1967 growfs.size = size; 1968 1969 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL); 1970 if (!error) { 1971 if (growfs.modified) 1972 printf("grown to %016jx\n", (intmax_t)growfs.size); 1973 else 1974 printf("no size change - %016jx\n", 1975 (intmax_t)growfs.size); 1976 } 1977 1978 return error; 1979 } 1980 1981 struct hammer2_link { 1982 TAILQ_ENTRY(hammer2_link) entry; 1983 hammer2_tid_t inum; 1984 uint64_t nlinks; 1985 char path[PATH_MAX]; 1986 }; 1987 1988 TAILQ_HEAD(hammer2_linkq, hammer2_link); 1989 1990 static void 1991 hammer2_linkq_init(struct hammer2_linkq *linkq) 1992 { 1993 TAILQ_INIT(linkq); 1994 } 1995 1996 static void 1997 hammer2_linkq_cleanup(struct hammer2_linkq *linkq) 1998 { 1999 struct hammer2_link *e; 2000 int count = 0; 2001 2002 /* 2003 * linkq must be empty at this point, or link count is broken. 2004 * Note that if an image was made by makefs, hardlinks in the source 2005 * directory become hardlinks in the image only if all links exist under 2006 * that directory, as makefs doesn't determine hardlink via link count. 2007 */ 2008 while ((e = TAILQ_FIRST(linkq)) != NULL) { 2009 count++; 2010 TAILQ_REMOVE(linkq, e, entry); 2011 free(e); 2012 } 2013 assert(TAILQ_EMPTY(linkq)); 2014 2015 if (count) 2016 errx(1, "%d link entries remained", count); 2017 } 2018 2019 static void 2020 hammer2_linkq_add(struct hammer2_linkq *linkq, hammer2_tid_t inum, 2021 uint64_t nlinks, const char *path) 2022 { 2023 struct hammer2_link *e; 2024 int count = 0; 2025 2026 e = ecalloc(1, sizeof(*e)); 2027 e->inum = inum; 2028 e->nlinks = nlinks; 2029 strlcpy(e->path, path, sizeof(e->path)); 2030 TAILQ_INSERT_TAIL(linkq, e, entry); 2031 2032 TAILQ_FOREACH(e, linkq, entry) 2033 if (e->inum == inum) 2034 count++; 2035 if (count > 1) 2036 errx(1, "%d link entries exist for inum %jd", 2037 count, (intmax_t)inum); 2038 } 2039 2040 static void 2041 hammer2_linkq_del(struct hammer2_linkq *linkq, hammer2_tid_t inum) 2042 { 2043 struct hammer2_link *e, *next; 2044 2045 TAILQ_FOREACH_MUTABLE(e, linkq, entry, next) 2046 if (e->inum == inum) { 2047 e->nlinks--; 2048 if (e->nlinks == 1) { 2049 TAILQ_REMOVE(linkq, e, entry); 2050 free(e); 2051 } 2052 } 2053 } 2054 2055 static void 2056 hammer2_utimes(struct m_vnode *vp, const char *f) 2057 { 2058 hammer2_inode_t *ip = VTOI(vp); 2059 struct timeval tv[2]; 2060 2061 hammer2_time_to_timeval(ip->meta.atime, &tv[0]); 2062 hammer2_time_to_timeval(ip->meta.mtime, &tv[1]); 2063 2064 utimes(f, tv); /* ignore failure */ 2065 } 2066 2067 static int 2068 hammer2_readx_directory(struct m_vnode *dvp, const char *dir, const char *name, 2069 struct hammer2_linkq *linkq) 2070 { 2071 struct m_vnode *vp; 2072 struct dirent *dp; 2073 struct stat st; 2074 char *buf, tmp[PATH_MAX]; 2075 off_t offset = 0; 2076 int ndirent = 0; 2077 int eofflag = 0; 2078 int i, error; 2079 2080 snprintf(tmp, sizeof(tmp), "%s/%s", dir, name); 2081 if (stat(tmp, &st) == -1 && mkdir(tmp, 0666) == -1) 2082 err(1, "failed to mkdir %s", tmp); 2083 2084 buf = calloc(1, HAMMER2_PBUFSIZE); 2085 2086 while (!eofflag) { 2087 error = hammer2_readdir(dvp, buf, HAMMER2_PBUFSIZE, &offset, 2088 &ndirent, &eofflag); 2089 if (error) 2090 errx(1, "failed to readdir"); 2091 dp = (void *)buf; 2092 2093 for (i = 0; i < ndirent; i++) { 2094 if (strcmp(dp->d_name, ".") && 2095 strcmp(dp->d_name, "..")) { 2096 error = hammer2_nresolve(dvp, &vp, dp->d_name, 2097 strlen(dp->d_name)); 2098 if (error) 2099 return error; 2100 error = hammer2_readx_handle(vp, tmp, 2101 dp->d_name, linkq); 2102 if (error) 2103 return error; 2104 } 2105 dp = (void *)((char *)dp + 2106 _DIRENT_RECLEN(dp->d_namlen)); 2107 } 2108 } 2109 2110 free(buf); 2111 hammer2_utimes(dvp, tmp); 2112 2113 return 0; 2114 } 2115 2116 static int 2117 hammer2_readx_link(struct m_vnode *vp, const char *src, const char *lnk, 2118 struct hammer2_linkq *linkq) 2119 { 2120 hammer2_inode_t *ip = VTOI(vp); 2121 struct stat st; 2122 int error; 2123 2124 if (!stat(lnk, &st)) { 2125 error = unlink(lnk); 2126 if (error) 2127 return error; 2128 } 2129 2130 error = link(src, lnk); 2131 if (error) 2132 return error; 2133 2134 hammer2_linkq_del(linkq, ip->meta.inum); 2135 2136 return 0; 2137 } 2138 2139 static int 2140 hammer2_readx_regfile(struct m_vnode *vp, const char *dir, const char *name, 2141 struct hammer2_linkq *linkq) 2142 { 2143 hammer2_inode_t *ip = VTOI(vp); 2144 struct hammer2_link *e; 2145 char *buf, out[PATH_MAX]; 2146 size_t resid, n; 2147 off_t offset; 2148 int fd, error; 2149 bool found = false; 2150 2151 snprintf(out, sizeof(out), "%s/%s", dir, name); 2152 2153 if (ip->meta.nlinks > 1) { 2154 TAILQ_FOREACH(e, linkq, entry) 2155 if (e->inum == ip->meta.inum) { 2156 found = true; 2157 error = hammer2_readx_link(vp, e->path, out, 2158 linkq); 2159 if (error == 0) 2160 return 0; 2161 /* ignore failure */ 2162 } 2163 if (!found) 2164 hammer2_linkq_add(linkq, ip->meta.inum, ip->meta.nlinks, 2165 out); 2166 } 2167 2168 fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666); 2169 if (fd == -1) 2170 err(1, "failed to create %s", out); 2171 2172 buf = calloc(1, HAMMER2_PBUFSIZE); 2173 resid = ip->meta.size; 2174 offset = 0; 2175 2176 while (resid > 0) { 2177 bzero(buf, HAMMER2_PBUFSIZE); 2178 error = hammer2_read(vp, buf, HAMMER2_PBUFSIZE, offset); 2179 if (error) 2180 errx(1, "failed to read from %s", name); 2181 2182 n = resid >= HAMMER2_PBUFSIZE ? HAMMER2_PBUFSIZE : resid; 2183 error = write(fd, buf, n); 2184 if (error == -1) 2185 err(1, "failed to write to %s", out); 2186 else if (error != n) 2187 return EINVAL; 2188 2189 resid -= n; 2190 offset += HAMMER2_PBUFSIZE; 2191 } 2192 fsync(fd); 2193 close(fd); 2194 2195 free(buf); 2196 hammer2_utimes(vp, out); 2197 2198 return 0; 2199 } 2200 2201 static int 2202 hammer2_readx_handle(struct m_vnode *vp, const char *dir, const char *name, 2203 struct hammer2_linkq *linkq) 2204 { 2205 hammer2_inode_t *ip = VTOI(vp); 2206 2207 switch (ip->meta.type) { 2208 case HAMMER2_OBJTYPE_DIRECTORY: 2209 return hammer2_readx_directory(vp, dir, name, linkq); 2210 case HAMMER2_OBJTYPE_REGFILE: 2211 return hammer2_readx_regfile(vp, dir, name, linkq); 2212 default: 2213 /* XXX */ 2214 printf("ignore inode %jd %s \"%s\"\n", 2215 (intmax_t)ip->meta.inum, 2216 hammer2_iptype_to_str(ip->meta.type), 2217 name); 2218 return 0; 2219 } 2220 return EINVAL; 2221 } 2222 2223 static int 2224 hammer2_readx(struct m_vnode *dvp, const char *dir, const char *f) 2225 { 2226 hammer2_inode_t *ip; 2227 struct hammer2_linkq linkq; 2228 struct m_vnode *vp; 2229 char *o, *p, *name; 2230 char tmp[PATH_MAX]; 2231 int error; 2232 2233 if (dir == NULL) 2234 return EINVAL; 2235 2236 assert(strlen(f) > 0); 2237 o = p = name = strdup(f); 2238 2239 error = trim_slash(p); 2240 if (error) 2241 return error; 2242 if (strlen(p) == 0) { 2243 vp = dvp; 2244 goto start_ioctl; 2245 } 2246 2247 while ((p = strchr(p, '/')) != NULL) { 2248 *p++ = 0; /* NULL terminate name */ 2249 if (!strcmp(name, ".")) { 2250 name = p; 2251 continue; 2252 } 2253 vp = NULL; 2254 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 2255 if (error) 2256 return error; 2257 2258 ip = VTOI(vp); 2259 switch (ip->meta.type) { 2260 case HAMMER2_OBJTYPE_DIRECTORY: 2261 break; 2262 case HAMMER2_OBJTYPE_SOFTLINK: 2263 bzero(tmp, sizeof(tmp)); 2264 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 2265 if (error) 2266 return error; 2267 if (!is_supported_link(tmp)) 2268 return EINVAL; 2269 strlcat(tmp, "/", sizeof(tmp)); 2270 strlcat(tmp, p, sizeof(tmp)); 2271 error = trim_slash(tmp); 2272 if (error) 2273 return error; 2274 p = name = tmp; 2275 continue; 2276 default: 2277 return EINVAL; 2278 } 2279 2280 dvp = vp; 2281 name = p; 2282 } 2283 2284 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 2285 if (error) 2286 return error; 2287 start_ioctl: 2288 hammer2_linkq_init(&linkq); 2289 error = hammer2_readx_handle(vp, dir, name, &linkq); 2290 hammer2_linkq_cleanup(&linkq); 2291 if (error) 2292 return error; 2293 2294 free(o); 2295 2296 return 0; 2297 } 2298 2299 static void 2300 assert_trim_slash(const char *input, const char *expected) 2301 { 2302 char tmp[PATH_MAX]; 2303 int error; 2304 2305 strlcpy(tmp, input, sizeof(tmp)); 2306 error = trim_slash(tmp); 2307 if (error) 2308 errx(1, "input \"%s\" error %d", input, error); 2309 2310 if (strncmp(tmp, expected, sizeof(tmp))) 2311 errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"", 2312 input, tmp, expected); 2313 } 2314 2315 static void 2316 unittest_trim_slash(void) 2317 { 2318 assert_trim_slash("", ""); 2319 assert_trim_slash("/", ""); 2320 assert_trim_slash("//", ""); 2321 assert_trim_slash("///", ""); 2322 2323 assert_trim_slash("makefs", "makefs"); 2324 assert_trim_slash("/makefs", "makefs"); 2325 assert_trim_slash("//makefs", "makefs"); 2326 assert_trim_slash("makefs/", "makefs"); 2327 assert_trim_slash("makefs//", "makefs"); 2328 assert_trim_slash("/makefs/", "makefs"); 2329 assert_trim_slash("//makefs//", "makefs"); 2330 2331 assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2"); 2332 assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2"); 2333 assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2"); 2334 assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2"); 2335 assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2"); 2336 assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2"); 2337 assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2"); 2338 assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2"); 2339 assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2"); 2340 assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2"); 2341 2342 APRINTF("success\n"); 2343 } 2344