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