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