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