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