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