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