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 static int hammer2_readx_handle(struct m_vnode *, const char *, const char *); 87 static int hammer2_readx(struct m_vnode *, const char *, const char *); 88 static void unittest_trim_slash(void); 89 90 fsnode *hammer2_curnode; 91 92 void 93 hammer2_prep_opts(fsinfo_t *fsopts) 94 { 95 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt)); 96 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 97 98 const option_t hammer2_options[] = { 99 /* newfs_hammer2(8) compatible options */ 100 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" }, 101 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" }, 102 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" }, 103 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" }, 104 /* makefs(8) specific options */ 105 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" }, 106 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32, 107 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" }, 108 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" }, 109 { 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0, 110 "emergency mode" }, 111 { 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" }, 112 { 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" }, 113 { 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" }, 114 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" }, 115 { 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" }, 116 { 'R', "Read", NULL, OPT_STRBUF, 0, 0, "offline read" }, 117 { .name = NULL }, 118 }; 119 120 hammer2_mkfs_init(opt); 121 122 /* make this tunable ? */ 123 assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT); 124 assert(opt->CheckType == HAMMER2_CHECK_XXHASH64); 125 126 /* force debug mode for mkfs */ 127 opt->DebugOpt = 1; 128 129 fsopts->fs_specific = h2_opt; 130 fsopts->fs_options = copy_opts(hammer2_options); 131 fsopts->sectorsize = DEV_BSIZE; 132 } 133 134 void 135 hammer2_cleanup_opts(fsinfo_t *fsopts) 136 { 137 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 138 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 139 140 hammer2_mkfs_cleanup(opt); 141 142 free(h2_opt); 143 free(fsopts->fs_options); 144 } 145 146 int 147 hammer2_parse_opts(const char *option, fsinfo_t *fsopts) 148 { 149 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 150 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 151 152 option_t *hammer2_options = fsopts->fs_options; 153 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */ 154 int i; 155 156 assert(option != NULL); 157 assert(fsopts != NULL); 158 159 if (debug & DEBUG_FS_PARSE_OPTS) 160 APRINTF("got `%s'\n", option); 161 162 i = set_option(hammer2_options, option, buf, sizeof(buf)); 163 if (i == -1) 164 return 0; 165 166 if (hammer2_options[i].name == NULL) 167 abort(); 168 169 switch (hammer2_options[i].letter) { 170 case 'b': 171 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 172 HAMMER2_BOOT_MAX_BYTES, 2); 173 break; 174 case 'r': 175 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 176 HAMMER2_AUX_MAX_BYTES, 2); 177 break; 178 case 'V': 179 if (strlen(buf) == 0) { 180 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET; 181 } else { 182 opt->Hammer2Version = strtol(buf, NULL, 0); 183 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN || 184 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP) 185 errx(1, "I don't understand how to format " 186 "HAMMER2 version %d", 187 opt->Hammer2Version); 188 } 189 break; 190 case 'L': 191 h2_opt->label_specified = 1; 192 if (strcasecmp(buf, "none") == 0) 193 break; 194 if (opt->NLabels >= MAXLABELS) 195 errx(1, "Limit of %d local labels", MAXLABELS - 1); 196 if (strlen(buf) == 0) 197 errx(1, "Volume label '%s' cannot be 0-length", buf); 198 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 199 errx(1, "Volume label '%s' is too long (%d chars max)", 200 buf, HAMMER2_INODE_MAXNAME - 1); 201 opt->Label[opt->NLabels++] = strdup(buf); 202 break; 203 case 'm': 204 if (strlen(buf) == 0) 205 errx(1, "Volume label '%s' cannot be 0-length", buf); 206 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 207 errx(1, "Volume label '%s' is too long (%d chars max)", 208 buf, HAMMER2_INODE_MAXNAME - 1); 209 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label)); 210 break; 211 case 'd': 212 hammer2_debug = strtoll(buf, NULL, 0); 213 break; 214 case 'P': 215 if (strlen(buf) == 0) 216 errx(1, "PFS argument '%s' cannot be 0-length", buf); 217 hammer2_parse_pfs_opts(buf, fsopts); 218 break; 219 case 'I': 220 if (strlen(buf) == 0) 221 errx(1, "Inode argument '%s' cannot be 0-length", buf); 222 hammer2_parse_inode_opts(buf, fsopts); 223 break; 224 case 'B': 225 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN; 226 break; 227 case 'D': 228 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY; 229 if (strlen(buf) == 0) 230 errx(1, "Destroy argument '%s' cannot be 0-length", buf); 231 if (buf[0] == '/') { 232 strlcpy(h2_opt->destroy_path, buf, 233 sizeof(h2_opt->destroy_path)); 234 } else if (strncmp(buf, "0x", 2) == 0 || 235 (buf[0] >= '0' && buf[0] <= '9')) { 236 h2_opt->destroy_inum = strtoull(buf, NULL, 0); 237 if (errno) 238 err(1, "strtoull"); 239 } else { 240 errx(1, "Invalid destroy argument %s", buf); 241 } 242 break; 243 case 'G': 244 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS; 245 break; 246 case 'R': 247 h2_opt->ioctl_cmd = HAMMER2IOC_READ; 248 if (strlen(buf) == 0) 249 errx(1, "Read argument '%s' cannot be 0-length", buf); 250 strlcpy(h2_opt->read_path, buf, sizeof(h2_opt->read_path)); 251 break; 252 default: 253 break; 254 } 255 256 if (hammer2_debug && h2_opt->ioctl_cmd) 257 unittest_trim_slash(); 258 259 return 1; 260 } 261 262 void 263 hammer2_makefs(const char *image, const char *dir, fsnode *root, 264 fsinfo_t *fsopts) 265 { 266 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 267 struct mount mp; 268 struct hammer2_mount_info info; 269 struct m_vnode devvp, *vroot; 270 hammer2_inode_t *iroot; 271 struct timeval start; 272 int error; 273 274 /* ioctl commands could have NULL dir / root */ 275 assert(image != NULL); 276 assert(fsopts != NULL); 277 278 if (debug & DEBUG_FS_MAKEFS) 279 APRINTF("image \"%s\" directory \"%s\" root %p\n", 280 image, dir, root); 281 282 /* validate tree and options */ 283 TIMER_START(start); 284 hammer2_validate(dir, root, fsopts); 285 TIMER_RESULTS(start, "hammer2_validate"); 286 287 if (h2_opt->ioctl_cmd) { 288 /* open existing image */ 289 fsopts->fd = open(image, O_RDWR); 290 if (fsopts->fd < 0) 291 err(1, "failed to open `%s'", image); 292 } else { 293 /* create image */ 294 TIMER_START(start); 295 if (hammer2_create_image(image, fsopts) == -1) 296 errx(1, "image file `%s' not created", image); 297 TIMER_RESULTS(start, "hammer2_create_image"); 298 } 299 assert(fsopts->fd > 0); 300 301 if (debug & DEBUG_FS_MAKEFS) 302 putchar('\n'); 303 304 /* vfs init */ 305 error = hammer2_vfs_init(); 306 if (error) 307 errx(1, "failed to vfs init, error %d", error); 308 309 /* mount image */ 310 memset(&devvp, 0, sizeof(devvp)); 311 devvp.fs = fsopts; 312 memset(&mp, 0, sizeof(mp)); 313 memset(&info, 0, sizeof(info)); 314 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info); 315 if (error) 316 errx(1, "failed to mount, error %d", error); 317 assert(mp.mnt_data); 318 319 /* get root vnode */ 320 vroot = NULL; 321 error = hammer2_vfs_root(&mp, &vroot); 322 if (error) 323 errx(1, "failed to get root vnode, error %d", error); 324 assert(vroot); 325 326 iroot = VTOI(vroot); 327 assert(iroot); 328 printf("root inode inum %lld, mode 0%o, refs %d\n", 329 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs); 330 331 if (h2_opt->emergency_mode) 332 hammer2_ioctl_emerg_mode(iroot, 1); 333 334 switch (h2_opt->ioctl_cmd) { 335 case HAMMER2IOC_VERSION_GET: 336 printf("version get `%s'\n", image); 337 TIMER_START(start); 338 error = hammer2_version_get(vroot); 339 if (error) 340 errx(1, "version get `%s' failed '%s'", image, 341 strerror(error)); 342 TIMER_RESULTS(start, "hammer2_version_get"); 343 break; 344 case HAMMER2IOC_PFS_GET: 345 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 346 TIMER_START(start); 347 error = hammer2_pfs_get(vroot); 348 if (error) 349 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 350 image, strerror(error)); 351 TIMER_RESULTS(start, "hammer2_pfs_get"); 352 break; 353 case HAMMER2IOC_PFS_LOOKUP: 354 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 355 TIMER_START(start); 356 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name); 357 if (error) 358 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 359 image, strerror(error)); 360 TIMER_RESULTS(start, "hammer2_pfs_lookup"); 361 break; 362 case HAMMER2IOC_PFS_CREATE: 363 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 364 TIMER_START(start); 365 error = hammer2_pfs_create(vroot, h2_opt->pfs_name); 366 if (error) 367 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 368 image, strerror(error)); 369 TIMER_RESULTS(start, "hammer2_pfs_create"); 370 break; 371 case HAMMER2IOC_PFS_DELETE: 372 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 373 TIMER_START(start); 374 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name); 375 if (error) 376 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 377 image, strerror(error)); 378 TIMER_RESULTS(start, "hammer2_pfs_delete"); 379 break; 380 case HAMMER2IOC_PFS_SNAPSHOT: 381 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 382 TIMER_START(start); 383 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name, 384 h2_opt->mount_label); 385 if (error) 386 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 387 image, strerror(error)); 388 TIMER_RESULTS(start, "hammer2_pfs_snapshot"); 389 break; 390 case HAMMER2IOC_INODE_GET: 391 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image); 392 TIMER_START(start); 393 error = hammer2_inode_getx(vroot, h2_opt->inode_path); 394 if (error) 395 errx(1, "inode %s `%s' failed '%s'", 396 h2_opt->inode_cmd_name, image, strerror(error)); 397 TIMER_RESULTS(start, "hammer2_inode_getx"); 398 break; 399 case HAMMER2IOC_INODE_SET: 400 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image); 401 TIMER_START(start); 402 if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) { 403 error = hammer2_inode_setcheck(vroot, 404 h2_opt->inode_path); 405 if (error) 406 errx(1, "inode %s `%s' failed '%s'", 407 h2_opt->inode_cmd_name, image, 408 strerror(error)); 409 } else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) { 410 error = hammer2_inode_setcomp(vroot, 411 h2_opt->inode_path); 412 if (error) 413 errx(1, "inode %s `%s' failed '%s'", 414 h2_opt->inode_cmd_name, image, 415 strerror(error)); 416 } else { 417 assert(0); 418 } 419 TIMER_RESULTS(start, "hammer2_inode_setx"); 420 break; 421 case HAMMER2IOC_BULKFREE_SCAN: 422 printf("bulkfree `%s'\n", image); 423 TIMER_START(start); 424 error = hammer2_bulkfree(vroot); 425 if (error) 426 errx(1, "bulkfree `%s' failed '%s'", image, 427 strerror(error)); 428 TIMER_RESULTS(start, "hammer2_bulkfree"); 429 break; 430 case HAMMER2IOC_DESTROY: 431 TIMER_START(start); 432 if (strlen(h2_opt->destroy_path)) { 433 printf("destroy `%s' in `%s'\n", 434 h2_opt->destroy_path, image); 435 error = hammer2_destroy_path(vroot, 436 h2_opt->destroy_path); 437 if (error) 438 errx(1, "destroy `%s' in `%s' failed '%s'", 439 h2_opt->destroy_path, image, 440 strerror(error)); 441 } else { 442 printf("destroy %lld in `%s'\n", 443 (long long)h2_opt->destroy_inum, image); 444 error = hammer2_destroy_inum(vroot, 445 h2_opt->destroy_inum); 446 if (error) 447 errx(1, "destroy %lld in `%s' failed '%s'", 448 (long long)h2_opt->destroy_inum, image, 449 strerror(error)); 450 } 451 TIMER_RESULTS(start, "hammer2_destroy"); 452 break; 453 case HAMMER2IOC_GROWFS: 454 printf("growfs `%s'\n", image); 455 TIMER_START(start); 456 error = hammer2_growfs(vroot, h2_opt->image_size); 457 if (error) 458 errx(1, "growfs `%s' failed '%s'", image, 459 strerror(error)); 460 TIMER_RESULTS(start, "hammer2_growfs"); 461 break; 462 case HAMMER2IOC_READ: 463 printf("read `%s'\n", image); 464 TIMER_START(start); 465 error = hammer2_readx(vroot, dir, h2_opt->read_path); 466 if (error) 467 errx(1, "read `%s' failed '%s'", image, 468 strerror(error)); 469 TIMER_RESULTS(start, "hammer2_readx"); 470 break; 471 default: 472 printf("populating `%s'\n", image); 473 TIMER_START(start); 474 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0)) 475 errx(1, "image file `%s' not populated", image); 476 TIMER_RESULTS(start, "hammer2_populate_dir"); 477 break; 478 } 479 480 /* unmount image */ 481 error = hammer2_vfs_unmount(&mp, 0); 482 if (error) 483 errx(1, "failed to unmount, error %d", error); 484 485 /* check leaked resource */ 486 if (vnode_count) 487 printf("XXX %lld vnode left\n", (long long)vnode_count); 488 if (hammer2_chain_allocs) 489 printf("XXX %ld chain left\n", hammer2_chain_allocs); 490 bcleanup(); 491 492 /* vfs uninit */ 493 error = hammer2_vfs_uninit(); 494 if (error) 495 errx(1, "failed to vfs uninit, error %d", error); 496 497 if (close(fsopts->fd) == -1) 498 err(1, "closing `%s'", image); 499 fsopts->fd = -1; 500 501 printf("image `%s' complete\n", image); 502 } 503 504 /* end of public functions */ 505 506 static void 507 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts) 508 { 509 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 510 char *o, *p; 511 size_t n; 512 513 o = p = strdup(buf); 514 p = strchr(p, ':'); 515 if (p != NULL) { 516 *p++ = 0; 517 n = strlen(p); 518 } else { 519 n = 0; 520 } 521 522 if (!strcmp(o, "get") || !strcmp(o, "list")) { 523 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET; 524 } else if (!strcmp(o, "lookup")) { 525 if (n == 0 || n > NAME_MAX) 526 errx(1, "invalid PFS name \"%s\"", p); 527 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP; 528 } else if (!strcmp(o, "create")) { 529 if (n == 0 || n > NAME_MAX) 530 errx(1, "invalid PFS name \"%s\"", p); 531 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE; 532 } else if (!strcmp(o, "delete")) { 533 if (n == 0 || n > NAME_MAX) 534 errx(1, "invalid PFS name \"%s\"", p); 535 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE; 536 } else if (!strcmp(o, "snapshot")) { 537 if (n > NAME_MAX) 538 errx(1, "invalid PFS name \"%s\"", p); 539 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT; 540 } else { 541 errx(1, "invalid PFS command \"%s\"", o); 542 } 543 544 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name)); 545 if (n > 0) 546 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name)); 547 548 free(o); 549 } 550 551 static void 552 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts) 553 { 554 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 555 char *o, *p; 556 size_t n; 557 558 o = p = strdup(buf); 559 p = strchr(p, ':'); 560 if (p != NULL) { 561 *p++ = 0; 562 n = strlen(p); 563 } else { 564 n = 0; 565 } 566 567 if (!strcmp(o, "get")) { 568 if (n == 0 || n > PATH_MAX) 569 errx(1, "invalid file path \"%s\"", p); 570 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET; 571 } else if (!strcmp(o, "setcheck")) { 572 if (n == 0 || n > PATH_MAX - 10) 573 errx(1, "invalid argument \"%s\"", p); 574 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET; 575 } else if (!strcmp(o, "setcomp")) { 576 if (n == 0 || n > PATH_MAX - 10) 577 errx(1, "invalid argument \"%s\"", p); 578 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET; 579 } else { 580 errx(1, "invalid inode command \"%s\"", o); 581 } 582 583 strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name)); 584 if (n > 0) 585 strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path)); 586 587 free(o); 588 } 589 590 static hammer2_off_t 591 hammer2_image_size(fsinfo_t *fsopts) 592 { 593 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 594 hammer2_off_t image_size, used_size = 0; 595 int num_level1, delta_num_level1; 596 597 /* use 4 volume headers by default */ 598 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */ 599 assert(num_level1 != 0); 600 assert(num_level1 <= 8); 601 602 /* add 4MiB segment for each level1 */ 603 used_size += HAMMER2_ZONE_SEG64 * num_level1; 604 605 /* add boot/aux area, but exact size unknown at this point */ 606 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES; 607 608 /* add data size */ 609 used_size += fsopts->size; 610 611 /* XXX add extra level1 for meta data and indirect blocks */ 612 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 613 614 /* XXX add extra level1 for safety */ 615 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10) 616 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 617 618 /* use 8GiB image size by default */ 619 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1; 620 printf("trying default image size %s\n", sizetostr(image_size)); 621 622 /* adjust if image size isn't large enough */ 623 if (used_size > image_size) { 624 /* determine extra level1 needed */ 625 delta_num_level1 = howmany(used_size - image_size, 626 HAMMER2_FREEMAP_LEVEL1_SIZE); 627 628 /* adjust used size with 4MiB segment for each extra level1 */ 629 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1; 630 631 /* adjust image size with extra level1 */ 632 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1; 633 printf("trying adjusted image size %s\n", 634 sizetostr(image_size)); 635 636 if (used_size > image_size) 637 errx(1, "invalid used_size %lld > image_size %lld", 638 (long long)used_size, (long long)image_size); 639 } 640 641 return image_size; 642 } 643 644 static const char * 645 hammer2_label_name(int label_type) 646 { 647 switch (label_type) { 648 case HAMMER2_LABEL_NONE: 649 return "NONE"; 650 case HAMMER2_LABEL_BOOT: 651 return "BOOT"; 652 case HAMMER2_LABEL_ROOT: 653 return "ROOT"; 654 case HAMMER2_LABEL_DATA: 655 return "DATA"; 656 default: 657 assert(0); 658 } 659 return NULL; 660 } 661 662 static void 663 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) 664 { 665 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 666 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 667 hammer2_off_t image_size = 0, minsize, maxsize; 668 const char *s; 669 670 /* ioctl commands could have NULL dir / root */ 671 assert(fsopts != NULL); 672 673 if (debug & DEBUG_FS_VALIDATE) { 674 APRINTF("before defaults set:\n"); 675 hammer2_dump_fsinfo(fsopts); 676 } 677 678 /* makefs only supports "DATA" for default PFS label */ 679 if (!h2_opt->label_specified) { 680 opt->DefaultLabelType = HAMMER2_LABEL_DATA; 681 s = hammer2_label_name(opt->DefaultLabelType); 682 printf("using default label \"%s\"\n", s); 683 } 684 685 /* set default mount PFS label */ 686 if (!strcmp(h2_opt->mount_label, "")) { 687 s = hammer2_label_name(HAMMER2_LABEL_DATA); 688 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label)); 689 printf("using default mount label \"%s\"\n", s); 690 } 691 692 /* set default number of volume headers */ 693 if (!h2_opt->num_volhdr) { 694 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS; 695 printf("using default %d volume headers\n", h2_opt->num_volhdr); 696 } 697 698 /* done if ioctl commands */ 699 if (h2_opt->ioctl_cmd) { 700 if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS) 701 goto ignore_size_dir; 702 else 703 goto done; 704 } 705 706 /* calculate data size */ 707 if (fsopts->size != 0) 708 fsopts->size = 0; /* shouldn't reach here to begin with */ 709 if (root == NULL) 710 errx(1, "fsnode tree not constructed"); 711 hammer2_size_dir(root, fsopts); 712 printf("estimated data size %s from %lld inode\n", 713 sizetostr(fsopts->size), (long long)fsopts->inodes); 714 715 /* determine image size from data size */ 716 image_size = hammer2_image_size(fsopts); 717 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 718 ignore_size_dir: 719 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 720 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 721 if (image_size < minsize) 722 image_size = minsize; 723 else if (maxsize > 0 && image_size > maxsize) 724 errx(1, "`%s' size of %lld is larger than the maxsize of %lld", 725 dir, (long long)image_size, (long long)maxsize); 726 727 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 728 h2_opt->image_size = image_size; 729 printf("using %s image size\n", sizetostr(h2_opt->image_size)); 730 done: 731 if (debug & DEBUG_FS_VALIDATE) { 732 APRINTF("after defaults set:\n"); 733 hammer2_dump_fsinfo(fsopts); 734 } 735 } 736 737 static void 738 hammer2_dump_fsinfo(fsinfo_t *fsopts) 739 { 740 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 741 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 742 int i; 743 char *s; 744 745 assert(fsopts != NULL); 746 747 APRINTF("fsinfo_t at %p\n", fsopts); 748 749 printf("\tinodes %lld\n", (long long)fsopts->inodes); 750 printf("\tsize %lld, minsize %lld, maxsize %lld\n", 751 (long long)fsopts->size, 752 (long long)fsopts->minsize, 753 (long long)fsopts->maxsize); 754 755 printf("\thammer2_debug 0x%x\n", hammer2_debug); 756 757 printf("\tlabel_specified %d\n", h2_opt->label_specified); 758 printf("\tmount_label \"%s\"\n", h2_opt->mount_label); 759 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr); 760 printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd); 761 printf("\temergency_mode %d\n", h2_opt->emergency_mode); 762 printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name); 763 printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name); 764 printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name); 765 printf("\tinode_path \"%s\"\n", h2_opt->inode_path); 766 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path); 767 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum); 768 printf("\tread_path \"%s\"\n", h2_opt->read_path); 769 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size); 770 771 printf("\tHammer2Version %d\n", opt->Hammer2Version); 772 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize); 773 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize); 774 printf("\tNLabels %d\n", opt->NLabels); 775 printf("\tCompType %d\n", opt->CompType); 776 printf("\tCheckType %d\n", opt->CheckType); 777 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType); 778 printf("\tDebugOpt %d\n", opt->DebugOpt); 779 780 s = NULL; 781 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s); 782 printf("\tHammer2_FSType \"%s\"\n", s); 783 s = NULL; 784 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s); 785 printf("\tHammer2_VolFSID \"%s\"\n", s); 786 s = NULL; 787 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s); 788 printf("\tHammer2_SupCLID \"%s\"\n", s); 789 s = NULL; 790 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s); 791 printf("\tHammer2_SupFSID \"%s\"\n", s); 792 793 for (i = 0; i < opt->NLabels; i++) { 794 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]); 795 s = NULL; 796 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s); 797 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s); 798 s = NULL; 799 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s); 800 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s); 801 } 802 803 free(s); 804 } 805 806 static int 807 hammer2_create_image(const char *image, fsinfo_t *fsopts) 808 { 809 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 810 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 811 char *av[] = { (char *)image, }; /* XXX support multi-volumes */ 812 char *buf; 813 int i, bufsize, oflags; 814 off_t bufrem; 815 816 assert(image != NULL); 817 assert(fsopts != NULL); 818 819 /* create image */ 820 oflags = O_RDWR | O_CREAT; 821 if (fsopts->offset == 0) 822 oflags |= O_TRUNC; 823 if ((fsopts->fd = open(image, oflags, 0666)) == -1) { 824 warn("can't open `%s' for writing", image); 825 return -1; 826 } 827 828 /* zero image */ 829 bufsize = HAMMER2_PBUFSIZE; 830 bufrem = h2_opt->image_size; 831 if (fsopts->sparse) { 832 if (ftruncate(fsopts->fd, bufrem) == -1) { 833 warn("sparse option disabled"); 834 fsopts->sparse = 0; 835 } 836 } 837 if (fsopts->sparse) { 838 /* File truncated at bufrem. Remaining is 0 */ 839 bufrem = 0; 840 buf = NULL; 841 } else { 842 if (debug & DEBUG_FS_CREATE_IMAGE) 843 APRINTF("zero-ing image `%s', %lld sectors, " 844 "using %d byte chunks\n", 845 image, (long long)bufrem, bufsize); 846 buf = ecalloc(1, bufsize); 847 } 848 849 if (fsopts->offset != 0) { 850 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) { 851 warn("can't seek"); 852 free(buf); 853 return -1; 854 } 855 } 856 857 while (bufrem > 0) { 858 i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); 859 if (i == -1) { 860 warn("zeroing image, %lld bytes to go", 861 (long long)bufrem); 862 free(buf); 863 return -1; 864 } 865 bufrem -= i; 866 } 867 if (buf) 868 free(buf); 869 870 /* make the file system */ 871 if (debug & DEBUG_FS_CREATE_IMAGE) 872 APRINTF("calling mkfs(\"%s\", ...)\n", image); 873 hammer2_mkfs(1, av, opt); /* success if returned */ 874 875 return fsopts->fd; 876 } 877 878 static off_t 879 hammer2_phys_size(off_t size) 880 { 881 off_t radix_size, phys_size = 0; 882 int i; 883 884 if (size > HAMMER2_PBUFSIZE) { 885 phys_size += rounddown(size, HAMMER2_PBUFSIZE); 886 size = size % HAMMER2_PBUFSIZE; 887 } 888 889 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) { 890 radix_size = 1UL << i; 891 if (radix_size >= size) { 892 phys_size += radix_size; 893 break; 894 } 895 } 896 897 return phys_size; 898 } 899 900 /* calculate data size */ 901 static void 902 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts) 903 { 904 fsnode *node; 905 906 assert(fsopts != NULL); 907 908 if (debug & DEBUG_FS_SIZE_DIR) 909 APRINTF("entry: bytes %lld inodes %lld\n", 910 (long long)fsopts->size, (long long)fsopts->inodes); 911 912 for (node = root; node != NULL; node = node->next) { 913 if (node == root) { /* we're at "." */ 914 assert(strcmp(node->name, ".") == 0); 915 } else if ((node->inode->flags & FI_SIZED) == 0) { 916 /* don't count duplicate names */ 917 node->inode->flags |= FI_SIZED; 918 if (debug & DEBUG_FS_SIZE_DIR_NODE) 919 APRINTF("`%s' size %lld\n", 920 node->name, 921 (long long)node->inode->st.st_size); 922 fsopts->inodes++; 923 fsopts->size += sizeof(hammer2_inode_data_t); 924 if (node->type == S_IFREG) { 925 size_t st_size = node->inode->st.st_size; 926 if (st_size > HAMMER2_EMBEDDED_BYTES) 927 fsopts->size += hammer2_phys_size(st_size); 928 } else if (node->type == S_IFLNK) { 929 size_t nlen = strlen(node->symlink); 930 if (nlen > HAMMER2_EMBEDDED_BYTES) 931 fsopts->size += hammer2_phys_size(nlen); 932 } 933 } 934 if (node->type == S_IFDIR) 935 hammer2_size_dir(node->child, fsopts); 936 } 937 938 if (debug & DEBUG_FS_SIZE_DIR) 939 APRINTF("exit: size %lld inodes %lld\n", 940 (long long)fsopts->size, (long long)fsopts->inodes); 941 } 942 943 static void 944 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp, 945 const fsnode *node, int depth, const char *msg) 946 { 947 if (debug & DEBUG_FS_POPULATE) { 948 if (1) { 949 int indent = depth * 2; 950 char *type; 951 if (S_ISDIR(node->type)) 952 type = "dir"; 953 else if (S_ISREG(node->type)) 954 type = "reg"; 955 else if (S_ISLNK(node->type)) 956 type = "lnk"; 957 else if (S_ISFIFO(node->type)) 958 type = "fifo"; 959 else 960 type = "???"; 961 printf("%*.*s", indent, indent, ""); 962 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n", 963 dvp, dvp ? VTOI(dvp)->refs : 0, 964 vp, vp ? VTOI(vp)->refs : 0, 965 node->name, type, msg); 966 } else { 967 char type; 968 if (S_ISDIR(node->type)) 969 type = 'd'; 970 else if (S_ISREG(node->type)) 971 type = 'r'; 972 else if (S_ISLNK(node->type)) 973 type = 'l'; 974 else if (S_ISFIFO(node->type)) 975 type = 'f'; 976 else 977 type = '?'; 978 printf("%c", type); 979 fflush(stdout); 980 } 981 } 982 } 983 984 static int 985 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root, 986 fsnode *parent, fsinfo_t *fsopts, int depth) 987 { 988 fsnode *cur; 989 struct m_vnode *vp; 990 struct stat st; 991 char f[MAXPATHLEN]; 992 const char *path; 993 int hardlink; 994 int error; 995 996 assert(dvp != NULL); 997 assert(dir != NULL); 998 assert(root != NULL); 999 assert(parent != NULL); 1000 assert(fsopts != NULL); 1001 1002 /* assert root directory */ 1003 assert(S_ISDIR(root->type)); 1004 assert(!strcmp(root->name, ".")); 1005 assert(!root->child); 1006 assert(!root->parent || root->parent->child == root); 1007 1008 hammer2_print(dvp, NULL, root, depth, "enter"); 1009 if (stat(dir, &st) == -1) 1010 err(1, "no such path %s", dir); 1011 if (!S_ISDIR(st.st_mode)) 1012 errx(1, "no such dir %s", dir); 1013 1014 for (cur = root->next; cur != NULL; cur = cur->next) { 1015 /* global variable for HAMMER2 vnops */ 1016 hammer2_curnode = cur; 1017 1018 /* construct source path */ 1019 if (cur->contents) { 1020 path = cur->contents; 1021 } else { 1022 if (S_ISDIR(cur->type)) { 1023 /* this should be same as root/path/name */ 1024 if (snprintf(f, sizeof(f), "%s/%s", 1025 dir, cur->name) >= (int)sizeof(f)) 1026 errx(1, "path %s too long", f); 1027 } else { 1028 if (snprintf(f, sizeof(f), "%s/%s/%s", 1029 cur->root, cur->path, cur->name) >= (int)sizeof(f)) 1030 errx(1, "path %s too long", f); 1031 } 1032 path = f; 1033 } 1034 if (S_ISLNK(cur->type)) { 1035 if (lstat(path, &st) == -1) 1036 err(1, "no such symlink %s", path); 1037 } else { 1038 if (stat(path, &st) == -1) 1039 err(1, "no such path %s", path); 1040 } 1041 1042 /* update node state */ 1043 if ((cur->inode->flags & FI_ALLOCATED) == 0) { 1044 cur->inode->flags |= FI_ALLOCATED; 1045 if (cur != root) 1046 cur->parent = parent; 1047 } 1048 1049 /* detect hardlink */ 1050 if (cur->inode->flags & FI_WRITTEN) { 1051 assert(!S_ISDIR(cur->type)); 1052 hardlink = 1; 1053 } else { 1054 hardlink = 0; 1055 } 1056 cur->inode->flags |= FI_WRITTEN; 1057 1058 /* make sure it doesn't exist yet */ 1059 vp = NULL; 1060 error = hammer2_nresolve(dvp, &vp, cur->name, 1061 strlen(cur->name)); 1062 if (!error) 1063 errx(1, "hammer2_nresolve(\"%s\") already exists", 1064 cur->name); 1065 hammer2_print(dvp, vp, cur, depth, "nresolve"); 1066 1067 /* if directory, mkdir and recurse */ 1068 if (S_ISDIR(cur->type)) { 1069 assert(cur->child); 1070 1071 vp = NULL; 1072 error = hammer2_nmkdir(dvp, &vp, cur->name, 1073 strlen(cur->name), cur->inode->st.st_mode); 1074 if (error) 1075 errx(1, "hammer2_nmkdir(\"%s\") failed: %s", 1076 cur->name, strerror(error)); 1077 assert(vp); 1078 hammer2_print(dvp, vp, cur, depth, "nmkdir"); 1079 1080 error = hammer2_populate_dir(vp, path, cur->child, cur, 1081 fsopts, depth + 1); 1082 if (error) 1083 errx(1, "failed to populate %s: %s", 1084 path, strerror(error)); 1085 cur->inode->param = vp; 1086 continue; 1087 } 1088 1089 /* if regular file, creat and write its data */ 1090 if (S_ISREG(cur->type) && !hardlink) { 1091 assert(cur->child == NULL); 1092 1093 vp = NULL; 1094 error = hammer2_ncreate(dvp, &vp, cur->name, 1095 strlen(cur->name), cur->inode->st.st_mode); 1096 if (error) 1097 errx(1, "hammer2_ncreate(\"%s\") failed: %s", 1098 cur->name, strerror(error)); 1099 assert(vp); 1100 hammer2_print(dvp, vp, cur, depth, "ncreate"); 1101 1102 error = hammer2_write_file(vp, path, cur); 1103 if (error) 1104 errx(1, "hammer2_write_file(\"%s\") failed: %s", 1105 path, strerror(error)); 1106 cur->inode->param = vp; 1107 continue; 1108 } 1109 1110 /* if symlink, create a symlink against target */ 1111 if (S_ISLNK(cur->type)) { 1112 assert(cur->child == NULL); 1113 1114 vp = NULL; 1115 error = hammer2_nsymlink(dvp, &vp, cur->name, 1116 strlen(cur->name), cur->symlink, 1117 cur->inode->st.st_mode); 1118 if (error) 1119 errx(1, "hammer2_nsymlink(\"%s\") failed: %s", 1120 cur->name, strerror(error)); 1121 assert(vp); 1122 hammer2_print(dvp, vp, cur, depth, "nsymlink"); 1123 cur->inode->param = vp; 1124 continue; 1125 } 1126 1127 /* if fifo, create a fifo */ 1128 if (S_ISFIFO(cur->type) && !hardlink) { 1129 assert(cur->child == NULL); 1130 1131 vp = NULL; 1132 error = hammer2_nmknod(dvp, &vp, cur->name, 1133 strlen(cur->name), VFIFO, cur->inode->st.st_mode); 1134 if (error) 1135 errx(1, "hammer2_nmknod(\"%s\") failed: %s", 1136 cur->name, strerror(error)); 1137 assert(vp); 1138 hammer2_print(dvp, vp, cur, depth, "nmknod"); 1139 cur->inode->param = vp; 1140 continue; 1141 } 1142 1143 /* if hardlink, creat a hardlink */ 1144 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) { 1145 char buf[64]; 1146 assert(cur->child == NULL); 1147 1148 /* source vnode must not be NULL */ 1149 vp = cur->inode->param; 1150 assert(vp); 1151 /* currently these conditions must be true */ 1152 assert(vp->v_data); 1153 assert(vp->v_type == VREG || vp->v_type == VFIFO); 1154 assert(vp->v_logical); 1155 assert(!vp->v_vflushed); 1156 assert(vp->v_malloced); 1157 assert(VTOI(vp)->refs > 0); 1158 1159 error = hammer2_nlink(dvp, vp, cur->name, 1160 strlen(cur->name)); 1161 if (error) 1162 errx(1, "hammer2_nlink(\"%s\") failed: %s", 1163 cur->name, strerror(error)); 1164 snprintf(buf, sizeof(buf), "nlink=%lld", 1165 (long long)VTOI(vp)->meta.nlinks); 1166 hammer2_print(dvp, vp, cur, depth, buf); 1167 continue; 1168 } 1169 1170 /* other types are unsupported */ 1171 printf("ignore %s 0%o\n", path, cur->type); 1172 } 1173 1174 return 0; 1175 } 1176 1177 static int 1178 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node) 1179 { 1180 struct stat *st = &node->inode->st; 1181 size_t nsize, bufsize; 1182 off_t offset; 1183 int fd, error; 1184 char *p; 1185 1186 nsize = st->st_size; 1187 if (nsize == 0) 1188 return 0; 1189 /* check nsize vs maximum file size */ 1190 1191 fd = open(path, O_RDONLY); 1192 if (fd < 0) 1193 err(1, "failed to open %s", path); 1194 1195 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 1196 if (p == MAP_FAILED) 1197 err(1, "failed to mmap %s", path); 1198 close(fd); 1199 1200 for (offset = 0; offset < nsize; ) { 1201 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE); 1202 assert(bufsize <= HAMMER2_PBUFSIZE); 1203 error = hammer2_write(vp, p + offset, bufsize, offset); 1204 if (error) 1205 errx(1, "failed to write to %s vnode: %s", 1206 path, strerror(error)); 1207 offset += bufsize; 1208 if (bufsize == HAMMER2_PBUFSIZE) 1209 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0); 1210 } 1211 munmap(p, nsize); 1212 1213 return 0; 1214 } 1215 1216 static int 1217 trim_char(char *p, char c) 1218 { 1219 char *o, tmp[PATH_MAX]; 1220 bool prev_was_c; 1221 size_t n; 1222 int i; 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 = %u\n", 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 error = trim_slash(p); 1618 if (error) 1619 return error; 1620 if (strlen(p) == 0 || strlen(check_algo_str) == 0) 1621 return EINVAL; 1622 1623 /* convert check_algo_str to check_algo_idx */ 1624 check_algo_idx = nitems(checks); 1625 while (--check_algo_idx >= 0) 1626 if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0) 1627 break; 1628 if (check_algo_idx < 0) { 1629 if (strcasecmp(check_algo_str, "default") == 0) { 1630 check_algo_str = "xxhash64"; 1631 check_algo_idx = HAMMER2_CHECK_XXHASH64; 1632 } else if (strcasecmp(check_algo_str, "disabled") == 0) { 1633 check_algo_str = "disabled"; 1634 check_algo_idx = HAMMER2_CHECK_DISABLED; 1635 } else { 1636 printf("invalid check_algo_str: %s\n", check_algo_str); 1637 return EINVAL; 1638 } 1639 } 1640 check_algo = HAMMER2_ENC_ALGO(check_algo_idx); 1641 printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str); 1642 1643 while ((p = strchr(p, '/')) != NULL) { 1644 *p++ = 0; /* NULL terminate name */ 1645 if (!strcmp(name, ".")) { 1646 name = p; 1647 continue; 1648 } 1649 vp = NULL; 1650 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1651 if (error) 1652 return error; 1653 1654 ip = VTOI(vp); 1655 switch (ip->meta.type) { 1656 case HAMMER2_OBJTYPE_DIRECTORY: 1657 break; 1658 case HAMMER2_OBJTYPE_SOFTLINK: 1659 bzero(tmp, sizeof(tmp)); 1660 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1661 if (error) 1662 return error; 1663 if (!is_supported_link(tmp)) 1664 return EINVAL; 1665 strlcat(tmp, "/", sizeof(tmp)); 1666 strlcat(tmp, p, sizeof(tmp)); 1667 error = trim_slash(tmp); 1668 if (error) 1669 return error; 1670 p = name = tmp; 1671 continue; 1672 default: 1673 return EINVAL; 1674 } 1675 1676 dvp = vp; 1677 name = p; 1678 } 1679 1680 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1681 if (error) 1682 return error; 1683 ip = VTOI(vp); 1684 1685 bzero(&inode, sizeof(inode)); 1686 error = hammer2_ioctl_inode_get(ip, &inode); 1687 if (error) 1688 return error; 1689 1690 inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK; 1691 inode.ip_data.meta.check_algo = check_algo; 1692 error = hammer2_ioctl_inode_set(ip, &inode); 1693 if (error) 1694 return error; 1695 1696 free(o); 1697 1698 return error; 1699 } 1700 1701 static int 1702 hammer2_inode_setcomp(struct m_vnode *dvp, const char *f) 1703 { 1704 hammer2_ioc_inode_t inode; 1705 hammer2_inode_t *ip; 1706 struct m_vnode *vp; 1707 char *o, *p, *name, *comp_algo_str, *comp_level_str; 1708 char tmp[PATH_MAX]; 1709 const char *comps[] = { "none", "autozero", "lz4", "zlib", }; 1710 int comp_algo_idx, comp_level_idx, error; 1711 uint8_t comp_algo, comp_level; 1712 1713 assert(strlen(f) > 0); 1714 o = p = strdup(f); 1715 1716 p = strrchr(p, ':'); 1717 if (p == NULL) 1718 return EINVAL; 1719 1720 *p++ = 0; /* NULL terminate comp_algo_str */ 1721 comp_level_str = p; 1722 p = o; 1723 1724 p = strrchr(p, ':'); 1725 if (p == NULL) { 1726 /* comp_level_str not specified */ 1727 comp_algo_str = comp_level_str; 1728 comp_level_str = NULL; 1729 } else { 1730 *p++ = 0; /* NULL terminate path */ 1731 comp_algo_str = p; 1732 } 1733 name = p = o; 1734 1735 error = trim_slash(p); 1736 if (error) 1737 return error; 1738 if (strlen(p) == 0 || strlen(comp_algo_str) == 0) 1739 return EINVAL; 1740 1741 /* convert comp_algo_str to comp_algo_idx */ 1742 comp_algo_idx = nitems(comps); 1743 while (--comp_algo_idx >= 0) 1744 if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0) 1745 break; 1746 if (comp_algo_idx < 0) { 1747 if (strcasecmp(comp_algo_str, "default") == 0) { 1748 comp_algo_str = "lz4"; 1749 comp_algo_idx = HAMMER2_COMP_LZ4; 1750 } else if (strcasecmp(comp_algo_str, "disabled") == 0) { 1751 comp_algo_str = "autozero"; 1752 comp_algo_idx = HAMMER2_COMP_AUTOZERO; 1753 } else { 1754 printf("invalid comp_algo_str: %s\n", comp_algo_str); 1755 return EINVAL; 1756 } 1757 } 1758 comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx); 1759 1760 /* convert comp_level_str to comp_level_idx */ 1761 if (comp_level_str == NULL) { 1762 comp_level_idx = 0; 1763 } else if (isdigit((int)comp_level_str[0])) { 1764 comp_level_idx = strtol(comp_level_str, NULL, 0); 1765 } else if (strcasecmp(comp_level_str, "default") == 0) { 1766 comp_level_idx = 0; 1767 } else { 1768 printf("invalid comp_level_str: %s\n", comp_level_str); 1769 return EINVAL; 1770 } 1771 if (comp_level_idx) { 1772 switch (comp_algo) { 1773 case HAMMER2_COMP_ZLIB: 1774 if (comp_level_idx < 6 || comp_level_idx > 9) { 1775 printf("unsupported comp_level %d for %s\n", 1776 comp_level_idx, comp_algo_str); 1777 return EINVAL; 1778 } 1779 break; 1780 default: 1781 printf("unsupported comp_level %d for %s\n", 1782 comp_level_idx, comp_algo_str); 1783 return EINVAL; 1784 } 1785 } 1786 comp_level = HAMMER2_ENC_LEVEL(comp_level_idx); 1787 printf("change %s to algo %d (%s) level %d\n", 1788 p, comp_algo, comp_algo_str, comp_level_idx); 1789 1790 while ((p = strchr(p, '/')) != NULL) { 1791 *p++ = 0; /* NULL terminate name */ 1792 if (!strcmp(name, ".")) { 1793 name = p; 1794 continue; 1795 } 1796 vp = NULL; 1797 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1798 if (error) 1799 return error; 1800 1801 ip = VTOI(vp); 1802 switch (ip->meta.type) { 1803 case HAMMER2_OBJTYPE_DIRECTORY: 1804 break; 1805 case HAMMER2_OBJTYPE_SOFTLINK: 1806 bzero(tmp, sizeof(tmp)); 1807 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1808 if (error) 1809 return error; 1810 if (!is_supported_link(tmp)) 1811 return EINVAL; 1812 strlcat(tmp, "/", sizeof(tmp)); 1813 strlcat(tmp, p, sizeof(tmp)); 1814 error = trim_slash(tmp); 1815 if (error) 1816 return error; 1817 p = name = tmp; 1818 continue; 1819 default: 1820 return EINVAL; 1821 } 1822 1823 dvp = vp; 1824 name = p; 1825 } 1826 1827 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1828 if (error) 1829 return error; 1830 ip = VTOI(vp); 1831 1832 bzero(&inode, sizeof(inode)); 1833 error = hammer2_ioctl_inode_get(ip, &inode); 1834 if (error) 1835 return error; 1836 1837 inode.flags |= HAMMER2IOC_INODE_FLAG_COMP; 1838 inode.ip_data.meta.comp_algo = comp_algo | comp_level; 1839 error = hammer2_ioctl_inode_set(ip, &inode); 1840 if (error) 1841 return error; 1842 1843 free(o); 1844 1845 return error; 1846 } 1847 1848 static int 1849 hammer2_bulkfree(struct m_vnode *vp) 1850 { 1851 hammer2_ioc_bulkfree_t bfi; 1852 size_t usermem; 1853 size_t usermem_size = sizeof(usermem); 1854 1855 bzero(&bfi, sizeof(bfi)); 1856 usermem = 0; 1857 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0) 1858 bfi.size = usermem / 16; 1859 else 1860 bfi.size = 0; 1861 if (bfi.size < 8192 * 1024) 1862 bfi.size = 8192 * 1024; 1863 1864 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi); 1865 } 1866 1867 static int 1868 hammer2_destroy_path(struct m_vnode *dvp, const char *f) 1869 { 1870 hammer2_ioc_destroy_t destroy; 1871 hammer2_inode_t *ip; 1872 struct m_vnode *vp; 1873 char *o, *p, *name; 1874 char tmp[PATH_MAX]; 1875 int error; 1876 1877 assert(strlen(f) > 0); 1878 o = p = name = strdup(f); 1879 1880 error = trim_slash(p); 1881 if (error) 1882 return error; 1883 if (strlen(p) == 0) 1884 return EINVAL; 1885 1886 while ((p = strchr(p, '/')) != NULL) { 1887 *p++ = 0; /* NULL terminate name */ 1888 if (!strcmp(name, ".")) { 1889 name = p; 1890 continue; 1891 } 1892 vp = NULL; 1893 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1894 if (error) 1895 return error; 1896 1897 ip = VTOI(vp); 1898 switch (ip->meta.type) { 1899 case HAMMER2_OBJTYPE_DIRECTORY: 1900 break; 1901 case HAMMER2_OBJTYPE_SOFTLINK: 1902 bzero(tmp, sizeof(tmp)); 1903 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 1904 if (error) 1905 return error; 1906 if (!is_supported_link(tmp)) 1907 return EINVAL; 1908 strlcat(tmp, "/", sizeof(tmp)); 1909 strlcat(tmp, p, sizeof(tmp)); 1910 error = trim_slash(tmp); 1911 if (error) 1912 return error; 1913 p = name = tmp; 1914 continue; 1915 default: 1916 return EINVAL; 1917 } 1918 1919 dvp = vp; 1920 name = p; 1921 } 1922 1923 /* XXX When does (or why does not) ioctl modify this inode ? */ 1924 hammer2_inode_modify(VTOI(dvp)); 1925 1926 bzero(&destroy, sizeof(destroy)); 1927 destroy.cmd = HAMMER2_DELETE_FILE; 1928 snprintf(destroy.path, sizeof(destroy.path), "%s", name); 1929 1930 printf("%s\t", f); 1931 fflush(stdout); 1932 1933 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy); 1934 if (error) 1935 printf("%s\n", strerror(error)); 1936 else 1937 printf("ok\n"); 1938 free(o); 1939 1940 return error; 1941 } 1942 1943 static int 1944 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum) 1945 { 1946 hammer2_ioc_destroy_t destroy; 1947 int error; 1948 1949 bzero(&destroy, sizeof(destroy)); 1950 destroy.cmd = HAMMER2_DELETE_INUM; 1951 destroy.inum = inum; 1952 1953 printf("%jd\t", (intmax_t)destroy.inum); 1954 fflush(stdout); 1955 1956 error = hammer2_ioctl_destroy(VTOI(vp), &destroy); 1957 if (error) 1958 printf("%s\n", strerror(error)); 1959 else 1960 printf("ok\n"); 1961 1962 return error; 1963 } 1964 1965 static int 1966 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size) 1967 { 1968 hammer2_ioc_growfs_t growfs; 1969 int error; 1970 1971 bzero(&growfs, sizeof(growfs)); 1972 growfs.size = size; 1973 1974 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL); 1975 if (!error) { 1976 if (growfs.modified) 1977 printf("grown to %016jx\n", (intmax_t)growfs.size); 1978 else 1979 printf("no size change - %016jx\n", 1980 (intmax_t)growfs.size); 1981 } 1982 1983 return error; 1984 } 1985 1986 static void 1987 hammer2_utimes(struct m_vnode *vp, const char *f) 1988 { 1989 hammer2_inode_t *ip = VTOI(vp); 1990 struct timeval tv[2]; 1991 1992 hammer2_time_to_timeval(ip->meta.atime, &tv[0]); 1993 hammer2_time_to_timeval(ip->meta.mtime, &tv[1]); 1994 1995 utimes(f, tv); /* ignore failure */ 1996 } 1997 1998 static int 1999 hammer2_readx_directory(struct m_vnode *dvp, const char *dir, const char *name) 2000 { 2001 struct m_vnode *vp; 2002 struct dirent *dp; 2003 struct stat st; 2004 char *buf, tmp[PATH_MAX]; 2005 off_t offset = 0; 2006 int ndirent = 0; 2007 int eofflag = 0; 2008 int i, error; 2009 2010 snprintf(tmp, sizeof(tmp), "%s/%s", dir, name); 2011 if (stat(tmp, &st) == -1 && mkdir(tmp, 0666) == -1) 2012 err(1, "failed to mkdir %s", tmp); 2013 2014 buf = calloc(1, HAMMER2_PBUFSIZE); 2015 2016 while (!eofflag) { 2017 error = hammer2_readdir(dvp, buf, HAMMER2_PBUFSIZE, &offset, 2018 &ndirent, &eofflag); 2019 if (error) 2020 errx(1, "failed to readdir"); 2021 dp = (void *)buf; 2022 2023 for (i = 0; i < ndirent; i++) { 2024 if (strcmp(dp->d_name, ".") && 2025 strcmp(dp->d_name, "..")) { 2026 error = hammer2_nresolve(dvp, &vp, dp->d_name, 2027 strlen(dp->d_name)); 2028 if (error) 2029 return error; 2030 error = hammer2_readx_handle(vp, tmp, 2031 dp->d_name); 2032 if (error) 2033 return error; 2034 } 2035 dp = (void *)((char *)dp + 2036 _DIRENT_RECLEN(dp->d_namlen)); 2037 } 2038 } 2039 2040 free(buf); 2041 hammer2_utimes(dvp, tmp); 2042 2043 return 0; 2044 } 2045 2046 static int 2047 hammer2_readx_regfile(struct m_vnode *vp, const char *dir, const char *name) 2048 { 2049 hammer2_inode_t *ip = VTOI(vp); 2050 char *buf, out[PATH_MAX]; 2051 size_t resid, n; 2052 off_t offset; 2053 int fd, error; 2054 2055 snprintf(out, sizeof(out), "%s/%s", dir, name); 2056 fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666); 2057 if (fd == -1) 2058 err(1, "failed to create %s", out); 2059 2060 buf = calloc(1, HAMMER2_PBUFSIZE); 2061 resid = ip->meta.size; 2062 offset = 0; 2063 2064 while (resid > 0) { 2065 bzero(buf, HAMMER2_PBUFSIZE); 2066 error = hammer2_read(vp, buf, HAMMER2_PBUFSIZE, offset); 2067 if (error) 2068 errx(1, "failed to read from %s", name); 2069 2070 n = resid >= HAMMER2_PBUFSIZE ? HAMMER2_PBUFSIZE : resid; 2071 error = write(fd, buf, n); 2072 if (error == -1) 2073 err(1, "failed to write to %s", out); 2074 else if (error != n) 2075 return EINVAL; 2076 2077 resid -= n; 2078 offset += HAMMER2_PBUFSIZE; 2079 } 2080 fsync(fd); 2081 close(fd); 2082 2083 free(buf); 2084 hammer2_utimes(vp, out); 2085 2086 return 0; 2087 } 2088 2089 static int 2090 hammer2_readx_handle(struct m_vnode *vp, const char *dir, const char *name) 2091 { 2092 hammer2_inode_t *ip = VTOI(vp); 2093 2094 switch (ip->meta.type) { 2095 case HAMMER2_OBJTYPE_DIRECTORY: 2096 return hammer2_readx_directory(vp, dir, name); 2097 case HAMMER2_OBJTYPE_REGFILE: 2098 return hammer2_readx_regfile(vp, dir, name); 2099 default: 2100 /* XXX */ 2101 printf("ignore inode %jd %s \"%s\"\n", 2102 (intmax_t)ip->meta.inum, 2103 hammer2_iptype_to_str(ip->meta.type), 2104 name); 2105 return 0; 2106 } 2107 return EINVAL; 2108 } 2109 2110 static int 2111 hammer2_readx(struct m_vnode *dvp, const char *dir, const char *f) 2112 { 2113 hammer2_inode_t *ip; 2114 struct m_vnode *vp; 2115 char *o, *p, *name; 2116 char tmp[PATH_MAX]; 2117 int error; 2118 2119 if (dir == NULL) 2120 return EINVAL; 2121 2122 assert(strlen(f) > 0); 2123 o = p = name = strdup(f); 2124 2125 error = trim_slash(p); 2126 if (error) 2127 return error; 2128 if (strlen(p) == 0) { 2129 vp = dvp; 2130 goto start_ioctl; 2131 } 2132 2133 while ((p = strchr(p, '/')) != NULL) { 2134 *p++ = 0; /* NULL terminate name */ 2135 if (!strcmp(name, ".")) { 2136 name = p; 2137 continue; 2138 } 2139 vp = NULL; 2140 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 2141 if (error) 2142 return error; 2143 2144 ip = VTOI(vp); 2145 switch (ip->meta.type) { 2146 case HAMMER2_OBJTYPE_DIRECTORY: 2147 break; 2148 case HAMMER2_OBJTYPE_SOFTLINK: 2149 bzero(tmp, sizeof(tmp)); 2150 error = hammer2_readlink(vp, tmp, sizeof(tmp)); 2151 if (error) 2152 return error; 2153 if (!is_supported_link(tmp)) 2154 return EINVAL; 2155 strlcat(tmp, "/", sizeof(tmp)); 2156 strlcat(tmp, p, sizeof(tmp)); 2157 error = trim_slash(tmp); 2158 if (error) 2159 return error; 2160 p = name = tmp; 2161 continue; 2162 default: 2163 return EINVAL; 2164 } 2165 2166 dvp = vp; 2167 name = p; 2168 } 2169 2170 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 2171 if (error) 2172 return error; 2173 start_ioctl: 2174 error = hammer2_readx_handle(vp, dir, name); 2175 if (error) 2176 return error; 2177 2178 free(o); 2179 2180 return 0; 2181 } 2182 2183 static void 2184 assert_trim_slash(const char *input, const char *expected) 2185 { 2186 char tmp[PATH_MAX]; 2187 int error; 2188 2189 strlcpy(tmp, input, sizeof(tmp)); 2190 error = trim_slash(tmp); 2191 if (error) 2192 errx(1, "input \"%s\" error %d", input, error); 2193 2194 if (strncmp(tmp, expected, sizeof(tmp))) 2195 errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"", 2196 input, tmp, expected); 2197 } 2198 2199 static void 2200 unittest_trim_slash(void) 2201 { 2202 assert_trim_slash("", ""); 2203 assert_trim_slash("/", ""); 2204 assert_trim_slash("//", ""); 2205 assert_trim_slash("///", ""); 2206 2207 assert_trim_slash("makefs", "makefs"); 2208 assert_trim_slash("/makefs", "makefs"); 2209 assert_trim_slash("//makefs", "makefs"); 2210 assert_trim_slash("makefs/", "makefs"); 2211 assert_trim_slash("makefs//", "makefs"); 2212 assert_trim_slash("/makefs/", "makefs"); 2213 assert_trim_slash("//makefs//", "makefs"); 2214 2215 assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2"); 2216 assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2"); 2217 assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2"); 2218 assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2"); 2219 assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2"); 2220 assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2"); 2221 assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2"); 2222 assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2"); 2223 assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2"); 2224 assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2"); 2225 2226 APRINTF("success\n"); 2227 } 2228