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 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <stdbool.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 #include <time.h> 51 #include <err.h> 52 #include <assert.h> 53 #include <util.h> 54 55 #include "makefs.h" 56 #include "hammer2.h" 57 58 #define APRINTF(X, ...) \ 59 printf("%s: " X, __func__, ## __VA_ARGS__) 60 61 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *); 62 static void hammer2_parse_inode_opts(const char *, fsinfo_t *); 63 static void hammer2_dump_fsinfo(fsinfo_t *); 64 static int hammer2_create_image(const char *, fsinfo_t *); 65 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *, 66 fsnode *, fsinfo_t *, int); 67 static void hammer2_validate(const char *, fsnode *, fsinfo_t *); 68 static void hammer2_size_dir(fsnode *, fsinfo_t *); 69 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *); 70 static int hammer2_version_get(struct m_vnode *); 71 static int hammer2_pfs_get(struct m_vnode *); 72 static int hammer2_pfs_lookup(struct m_vnode *, const char *); 73 static int hammer2_pfs_create(struct m_vnode *, const char *); 74 static int hammer2_pfs_delete(struct m_vnode *, const char *); 75 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *); 76 static int hammer2_inode_getx(struct m_vnode *vp, const char *); 77 static int hammer2_bulkfree(struct m_vnode *); 78 static int hammer2_destroy_path(struct m_vnode *, const char *); 79 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t); 80 static int hammer2_growfs(struct m_vnode *, hammer2_off_t); 81 82 fsnode *hammer2_curnode; 83 84 void 85 hammer2_prep_opts(fsinfo_t *fsopts) 86 { 87 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt)); 88 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 89 90 const option_t hammer2_options[] = { 91 /* newfs_hammer2(8) compatible options */ 92 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" }, 93 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" }, 94 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" }, 95 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" }, 96 /* makefs(8) specific options */ 97 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" }, 98 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32, 99 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" }, 100 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" }, 101 { 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0, 102 "emergency mode" }, 103 { 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" }, 104 { 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" }, 105 { 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" }, 106 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" }, 107 { 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" }, 108 { .name = NULL }, 109 }; 110 111 hammer2_mkfs_init(opt); 112 113 /* make this tunable ? */ 114 assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT); 115 assert(opt->CheckType == HAMMER2_CHECK_XXHASH64); 116 117 /* force debug mode for mkfs */ 118 opt->DebugOpt = 1; 119 120 fsopts->fs_specific = h2_opt; 121 fsopts->fs_options = copy_opts(hammer2_options); 122 fsopts->sectorsize = DEV_BSIZE; 123 } 124 125 void 126 hammer2_cleanup_opts(fsinfo_t *fsopts) 127 { 128 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 129 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 130 131 hammer2_mkfs_cleanup(opt); 132 133 free(h2_opt); 134 free(fsopts->fs_options); 135 } 136 137 int 138 hammer2_parse_opts(const char *option, 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 option_t *hammer2_options = fsopts->fs_options; 144 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */ 145 int i; 146 147 assert(option != NULL); 148 assert(fsopts != NULL); 149 150 if (debug & DEBUG_FS_PARSE_OPTS) 151 APRINTF("got `%s'\n", option); 152 153 i = set_option(hammer2_options, option, buf, sizeof(buf)); 154 if (i == -1) 155 return 0; 156 157 if (hammer2_options[i].name == NULL) 158 abort(); 159 160 switch (hammer2_options[i].letter) { 161 case 'b': 162 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 163 HAMMER2_BOOT_MAX_BYTES, 2); 164 break; 165 case 'r': 166 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN, 167 HAMMER2_AUX_MAX_BYTES, 2); 168 break; 169 case 'V': 170 if (strlen(buf) == 0) { 171 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET; 172 } else { 173 opt->Hammer2Version = strtol(buf, NULL, 0); 174 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN || 175 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP) 176 errx(1, "I don't understand how to format " 177 "HAMMER2 version %d", 178 opt->Hammer2Version); 179 } 180 break; 181 case 'L': 182 h2_opt->label_specified = 1; 183 if (strcasecmp(buf, "none") == 0) 184 break; 185 if (opt->NLabels >= MAXLABELS) 186 errx(1, "Limit of %d local labels", MAXLABELS - 1); 187 if (strlen(buf) == 0) 188 errx(1, "Volume label '%s' cannot be 0-length", buf); 189 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 190 errx(1, "Volume label '%s' is too long (%d chars max)", 191 buf, HAMMER2_INODE_MAXNAME - 1); 192 opt->Label[opt->NLabels++] = strdup(buf); 193 break; 194 case 'm': 195 if (strlen(buf) == 0) 196 errx(1, "Volume label '%s' cannot be 0-length", buf); 197 if (strlen(buf) >= HAMMER2_INODE_MAXNAME) 198 errx(1, "Volume label '%s' is too long (%d chars max)", 199 buf, HAMMER2_INODE_MAXNAME - 1); 200 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label)); 201 break; 202 case 'd': 203 hammer2_debug = strtoll(buf, NULL, 0); 204 break; 205 case 'P': 206 if (strlen(buf) == 0) 207 errx(1, "PFS argument '%s' cannot be 0-length", buf); 208 hammer2_parse_pfs_opts(buf, fsopts); 209 break; 210 case 'I': 211 if (strlen(buf) == 0) 212 errx(1, "Inode argument '%s' cannot be 0-length", buf); 213 hammer2_parse_inode_opts(buf, fsopts); 214 break; 215 case 'B': 216 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN; 217 break; 218 case 'D': 219 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY; 220 if (strlen(buf) == 0) 221 errx(1, "Destroy argument '%s' cannot be 0-length", buf); 222 if (buf[0] == '/') { 223 strlcpy(h2_opt->destroy_path, buf + 1, 224 sizeof(h2_opt->destroy_path)); 225 } else if (strncmp(buf, "0x", 2) == 0 || 226 (buf[0] >= '0' && buf[0] <= '9')) { 227 h2_opt->destroy_inum = strtoull(buf, NULL, 0); 228 if (errno) 229 err(1, "strtoull"); 230 } else { 231 errx(1, "Invalid destroy argument %s", buf); 232 } 233 break; 234 case 'G': 235 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS; 236 break; 237 default: 238 break; 239 } 240 241 return 1; 242 } 243 244 void 245 hammer2_makefs(const char *image, const char *dir, fsnode *root, 246 fsinfo_t *fsopts) 247 { 248 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 249 struct mount mp; 250 struct hammer2_mount_info info; 251 struct m_vnode devvp, *vroot; 252 hammer2_inode_t *iroot; 253 struct timeval start; 254 int error; 255 256 /* ioctl commands could have NULL root */ 257 assert(image != NULL); 258 assert(dir != NULL); 259 assert(fsopts != NULL); 260 261 if (debug & DEBUG_FS_MAKEFS) 262 APRINTF("image \"%s\" directory \"%s\" root %p\n", 263 image, dir, root); 264 265 /* validate tree and options */ 266 TIMER_START(start); 267 hammer2_validate(dir, root, fsopts); 268 TIMER_RESULTS(start, "hammer2_validate"); 269 270 if (h2_opt->ioctl_cmd) { 271 /* open existing image */ 272 fsopts->fd = open(image, O_RDWR); 273 if (fsopts->fd < 0) 274 err(1, "failed to open `%s'", image); 275 } else { 276 /* create image */ 277 TIMER_START(start); 278 if (hammer2_create_image(image, fsopts) == -1) 279 errx(1, "image file `%s' not created", image); 280 TIMER_RESULTS(start, "hammer2_create_image"); 281 } 282 assert(fsopts->fd > 0); 283 284 if (debug & DEBUG_FS_MAKEFS) 285 putchar('\n'); 286 287 /* vfs init */ 288 error = hammer2_vfs_init(); 289 if (error) 290 errx(1, "failed to vfs init, error %d", error); 291 292 /* mount image */ 293 memset(&devvp, 0, sizeof(devvp)); 294 devvp.fs = fsopts; 295 memset(&mp, 0, sizeof(mp)); 296 memset(&info, 0, sizeof(info)); 297 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info); 298 if (error) 299 errx(1, "failed to mount, error %d", error); 300 assert(mp.mnt_data); 301 302 /* get root vnode */ 303 vroot = NULL; 304 error = hammer2_vfs_root(&mp, &vroot); 305 if (error) 306 errx(1, "failed to get root vnode, error %d", error); 307 assert(vroot); 308 309 iroot = VTOI(vroot); 310 assert(iroot); 311 printf("root inode inum %lld, mode 0%o, refs %d\n", 312 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs); 313 314 if (h2_opt->emergency_mode) 315 hammer2_ioctl_emerg_mode(iroot, 1); 316 317 switch (h2_opt->ioctl_cmd) { 318 case HAMMER2IOC_VERSION_GET: 319 printf("version get `%s'\n", image); 320 TIMER_START(start); 321 error = hammer2_version_get(vroot); 322 if (error) 323 errx(1, "version get `%s' failed '%s'", image, 324 strerror(error)); 325 TIMER_RESULTS(start, "hammer2_version_get"); 326 break; 327 case HAMMER2IOC_PFS_GET: 328 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 329 TIMER_START(start); 330 error = hammer2_pfs_get(vroot); 331 if (error) 332 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 333 image, strerror(error)); 334 TIMER_RESULTS(start, "hammer2_pfs_get"); 335 break; 336 case HAMMER2IOC_PFS_LOOKUP: 337 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 338 TIMER_START(start); 339 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name); 340 if (error) 341 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 342 image, strerror(error)); 343 TIMER_RESULTS(start, "hammer2_pfs_lookup"); 344 break; 345 case HAMMER2IOC_PFS_CREATE: 346 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 347 TIMER_START(start); 348 error = hammer2_pfs_create(vroot, h2_opt->pfs_name); 349 if (error) 350 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 351 image, strerror(error)); 352 TIMER_RESULTS(start, "hammer2_pfs_create"); 353 break; 354 case HAMMER2IOC_PFS_DELETE: 355 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 356 TIMER_START(start); 357 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name); 358 if (error) 359 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 360 image, strerror(error)); 361 TIMER_RESULTS(start, "hammer2_pfs_delete"); 362 break; 363 case HAMMER2IOC_PFS_SNAPSHOT: 364 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image); 365 TIMER_START(start); 366 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name, 367 h2_opt->mount_label); 368 if (error) 369 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name, 370 image, strerror(error)); 371 TIMER_RESULTS(start, "hammer2_pfs_snapshot"); 372 break; 373 case HAMMER2IOC_INODE_GET: 374 printf("inode get `%s'\n", image); 375 TIMER_START(start); 376 error = hammer2_inode_getx(vroot, h2_opt->inode_path); 377 if (error) 378 errx(1, "inode get `%s' failed '%s'", image, 379 strerror(error)); 380 TIMER_RESULTS(start, "hammer2_inode_getx"); 381 break; 382 case HAMMER2IOC_BULKFREE_SCAN: 383 printf("bulkfree `%s'\n", image); 384 TIMER_START(start); 385 error = hammer2_bulkfree(vroot); 386 if (error) 387 errx(1, "bulkfree `%s' failed '%s'", image, 388 strerror(error)); 389 TIMER_RESULTS(start, "hammer2_bulkfree"); 390 break; 391 case HAMMER2IOC_DESTROY: 392 TIMER_START(start); 393 if (strlen(h2_opt->destroy_path)) { 394 printf("destroy `%s' in `%s'\n", 395 h2_opt->destroy_path, image); 396 error = hammer2_destroy_path(vroot, 397 h2_opt->destroy_path); 398 if (error) 399 errx(1, "destroy `%s' in `%s' failed '%s'", 400 h2_opt->destroy_path, image, 401 strerror(error)); 402 } else { 403 printf("destroy %lld in `%s'\n", 404 (long long)h2_opt->destroy_inum, image); 405 error = hammer2_destroy_inum(vroot, 406 h2_opt->destroy_inum); 407 if (error) 408 errx(1, "destroy %lld in `%s' failed '%s'", 409 (long long)h2_opt->destroy_inum, image, 410 strerror(error)); 411 } 412 TIMER_RESULTS(start, "hammer2_destroy"); 413 break; 414 case HAMMER2IOC_GROWFS: 415 printf("growfs `%s'\n", image); 416 TIMER_START(start); 417 error = hammer2_growfs(vroot, h2_opt->image_size); 418 if (error) 419 errx(1, "growfs `%s' failed '%s'", image, 420 strerror(error)); 421 TIMER_RESULTS(start, "hammer2_growfs"); 422 break; 423 default: 424 printf("populating `%s'\n", image); 425 TIMER_START(start); 426 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0)) 427 errx(1, "image file `%s' not populated", image); 428 TIMER_RESULTS(start, "hammer2_populate_dir"); 429 break; 430 } 431 432 /* unmount image */ 433 error = hammer2_vfs_unmount(&mp, 0); 434 if (error) 435 errx(1, "failed to unmount, error %d", error); 436 437 /* check leaked resource */ 438 if (vnode_count) 439 printf("XXX %lld vnode left\n", (long long)vnode_count); 440 if (hammer2_chain_allocs) 441 printf("XXX %ld chain left\n", hammer2_chain_allocs); 442 bcleanup(); 443 444 /* vfs uninit */ 445 error = hammer2_vfs_uninit(); 446 if (error) 447 errx(1, "failed to vfs uninit, error %d", error); 448 449 if (close(fsopts->fd) == -1) 450 err(1, "closing `%s'", image); 451 fsopts->fd = -1; 452 453 printf("image `%s' complete\n", image); 454 } 455 456 /* end of public functions */ 457 458 static void 459 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts) 460 { 461 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 462 char *o, *p; 463 size_t n; 464 465 o = p = strdup(buf); 466 p = strchr(p, ':'); 467 if (p != NULL) { 468 *p++ = 0; 469 n = strlen(p); 470 } else { 471 n = 0; 472 } 473 474 if (!strcmp(o, "get") || !strcmp(o, "list")) { 475 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET; 476 } else if (!strcmp(o, "lookup")) { 477 if (n == 0 || n > NAME_MAX) 478 errx(1, "invalid PFS name \"%s\"", p); 479 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP; 480 } else if (!strcmp(o, "create")) { 481 if (n == 0 || n > NAME_MAX) 482 errx(1, "invalid PFS name \"%s\"", p); 483 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE; 484 } else if (!strcmp(o, "delete")) { 485 if (n == 0 || n > NAME_MAX) 486 errx(1, "invalid PFS name \"%s\"", p); 487 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE; 488 } else if (!strcmp(o, "snapshot")) { 489 if (n > NAME_MAX) 490 errx(1, "invalid PFS name \"%s\"", p); 491 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT; 492 } else { 493 errx(1, "invalid PFS command \"%s\"", o); 494 } 495 496 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name)); 497 if (n > 0) 498 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name)); 499 500 free(o); 501 } 502 503 static void 504 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts) 505 { 506 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 507 char *o, *p; 508 size_t n; 509 510 o = p = strdup(buf); 511 p = strchr(p, ':'); 512 if (p != NULL) { 513 *p++ = 0; 514 n = strlen(p); 515 } else { 516 n = 0; 517 } 518 519 if (!strcmp(o, "get")) { 520 if (n == 0 || n > PATH_MAX) 521 errx(1, "invalid file path \"%s\"", p); 522 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET; 523 } else { 524 errx(1, "invalid inode command \"%s\"", o); 525 } 526 527 if (n > 0) { 528 if (p[0] == '/') 529 p++; 530 strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path)); 531 } 532 533 free(o); 534 } 535 536 static hammer2_off_t 537 hammer2_image_size(fsinfo_t *fsopts) 538 { 539 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 540 hammer2_off_t image_size, used_size = 0; 541 int num_level1, delta_num_level1; 542 543 /* use 4 volume headers by default */ 544 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */ 545 assert(num_level1 != 0); 546 assert(num_level1 <= 8); 547 548 /* add 4MiB segment for each level1 */ 549 used_size += HAMMER2_ZONE_SEG64 * num_level1; 550 551 /* add boot/aux area, but exact size unknown at this point */ 552 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES; 553 554 /* add data size */ 555 used_size += fsopts->size; 556 557 /* XXX add extra level1 for meta data and indirect blocks */ 558 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 559 560 /* XXX add extra level1 for safety */ 561 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10) 562 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE; 563 564 /* use 8GiB image size by default */ 565 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1; 566 printf("trying default image size %s\n", sizetostr(image_size)); 567 568 /* adjust if image size isn't large enough */ 569 if (used_size > image_size) { 570 /* determine extra level1 needed */ 571 delta_num_level1 = howmany(used_size - image_size, 572 HAMMER2_FREEMAP_LEVEL1_SIZE); 573 574 /* adjust used size with 4MiB segment for each extra level1 */ 575 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1; 576 577 /* adjust image size with extra level1 */ 578 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1; 579 printf("trying adjusted image size %s\n", 580 sizetostr(image_size)); 581 582 if (used_size > image_size) 583 errx(1, "invalid used_size %lld > image_size %lld", 584 (long long)used_size, (long long)image_size); 585 } 586 587 return image_size; 588 } 589 590 static const char * 591 hammer2_label_name(int label_type) 592 { 593 switch (label_type) { 594 case HAMMER2_LABEL_NONE: 595 return "NONE"; 596 case HAMMER2_LABEL_BOOT: 597 return "BOOT"; 598 case HAMMER2_LABEL_ROOT: 599 return "ROOT"; 600 case HAMMER2_LABEL_DATA: 601 return "DATA"; 602 default: 603 assert(0); 604 } 605 return NULL; 606 } 607 608 static void 609 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) 610 { 611 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 612 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 613 hammer2_off_t image_size = 0, minsize, maxsize; 614 const char *s; 615 616 assert(dir != NULL); 617 assert(fsopts != NULL); 618 619 if (debug & DEBUG_FS_VALIDATE) { 620 APRINTF("before defaults set:\n"); 621 hammer2_dump_fsinfo(fsopts); 622 } 623 624 /* makefs only supports "DATA" for default PFS label */ 625 if (!h2_opt->label_specified) { 626 opt->DefaultLabelType = HAMMER2_LABEL_DATA; 627 s = hammer2_label_name(opt->DefaultLabelType); 628 printf("using default label \"%s\"\n", s); 629 } 630 631 /* set default mount PFS label */ 632 if (!strcmp(h2_opt->mount_label, "")) { 633 s = hammer2_label_name(HAMMER2_LABEL_DATA); 634 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label)); 635 printf("using default mount label \"%s\"\n", s); 636 } 637 638 /* set default number of volume headers */ 639 if (!h2_opt->num_volhdr) { 640 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS; 641 printf("using default %d volume headers\n", h2_opt->num_volhdr); 642 } 643 644 /* done if ioctl commands */ 645 if (h2_opt->ioctl_cmd) { 646 if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS) 647 goto ignore_size_dir; 648 else 649 goto done; 650 } 651 652 /* calculate data size */ 653 if (fsopts->size != 0) 654 fsopts->size = 0; /* shouldn't reach here to begin with */ 655 if (root == NULL) 656 errx(1, "fsnode tree not constructed"); 657 hammer2_size_dir(root, fsopts); 658 printf("estimated data size %s from %lld inode\n", 659 sizetostr(fsopts->size), (long long)fsopts->inodes); 660 661 /* determine image size from data size */ 662 image_size = hammer2_image_size(fsopts); 663 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 664 ignore_size_dir: 665 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 666 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE); 667 if (image_size < minsize) 668 image_size = minsize; 669 else if (maxsize > 0 && image_size > maxsize) 670 errx(1, "`%s' size of %lld is larger than the maxsize of %lld", 671 dir, (long long)image_size, (long long)maxsize); 672 673 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0); 674 h2_opt->image_size = image_size; 675 printf("using %s image size\n", sizetostr(h2_opt->image_size)); 676 done: 677 if (debug & DEBUG_FS_VALIDATE) { 678 APRINTF("after defaults set:\n"); 679 hammer2_dump_fsinfo(fsopts); 680 } 681 } 682 683 static void 684 hammer2_dump_fsinfo(fsinfo_t *fsopts) 685 { 686 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 687 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 688 int i; 689 char *s; 690 691 assert(fsopts != NULL); 692 693 APRINTF("fsinfo_t at %p\n", fsopts); 694 695 printf("\tinodes %lld\n", (long long)fsopts->inodes); 696 printf("\tsize %lld, minsize %lld, maxsize %lld\n", 697 (long long)fsopts->size, 698 (long long)fsopts->minsize, 699 (long long)fsopts->maxsize); 700 701 printf("\thammer2_debug 0x%x\n", hammer2_debug); 702 703 printf("\tlabel_specified %d\n", h2_opt->label_specified); 704 printf("\tmount_label \"%s\"\n", h2_opt->mount_label); 705 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr); 706 printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd); 707 printf("\temergency_mode %d\n", h2_opt->emergency_mode); 708 printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name); 709 printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name); 710 printf("\tinode_path \"%s\"\n", h2_opt->inode_path); 711 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path); 712 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum); 713 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size); 714 715 printf("\tHammer2Version %d\n", opt->Hammer2Version); 716 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize); 717 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize); 718 printf("\tNLabels %d\n", opt->NLabels); 719 printf("\tCompType %d\n", opt->CompType); 720 printf("\tCheckType %d\n", opt->CheckType); 721 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType); 722 printf("\tDebugOpt %d\n", opt->DebugOpt); 723 724 s = NULL; 725 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s); 726 printf("\tHammer2_FSType \"%s\"\n", s); 727 s = NULL; 728 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s); 729 printf("\tHammer2_VolFSID \"%s\"\n", s); 730 s = NULL; 731 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s); 732 printf("\tHammer2_SupCLID \"%s\"\n", s); 733 s = NULL; 734 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s); 735 printf("\tHammer2_SupFSID \"%s\"\n", s); 736 737 for (i = 0; i < opt->NLabels; i++) { 738 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]); 739 s = NULL; 740 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s); 741 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s); 742 s = NULL; 743 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s); 744 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s); 745 } 746 747 free(s); 748 } 749 750 static int 751 hammer2_create_image(const char *image, fsinfo_t *fsopts) 752 { 753 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific; 754 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options; 755 char *av[] = { (char *)image, }; /* XXX support multi-volumes */ 756 char *buf; 757 int i, bufsize, oflags; 758 off_t bufrem; 759 760 assert(image != NULL); 761 assert(fsopts != NULL); 762 763 /* create image */ 764 oflags = O_RDWR | O_CREAT; 765 if (fsopts->offset == 0) 766 oflags |= O_TRUNC; 767 if ((fsopts->fd = open(image, oflags, 0666)) == -1) { 768 warn("can't open `%s' for writing", image); 769 return -1; 770 } 771 772 /* zero image */ 773 bufsize = HAMMER2_PBUFSIZE; 774 bufrem = h2_opt->image_size; 775 if (fsopts->sparse) { 776 if (ftruncate(fsopts->fd, bufrem) == -1) { 777 warn("sparse option disabled"); 778 fsopts->sparse = 0; 779 } 780 } 781 if (fsopts->sparse) { 782 /* File truncated at bufrem. Remaining is 0 */ 783 bufrem = 0; 784 buf = NULL; 785 } else { 786 if (debug & DEBUG_FS_CREATE_IMAGE) 787 APRINTF("zero-ing image `%s', %lld sectors, " 788 "using %d byte chunks\n", 789 image, (long long)bufrem, bufsize); 790 buf = ecalloc(1, bufsize); 791 } 792 793 if (fsopts->offset != 0) { 794 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) { 795 warn("can't seek"); 796 free(buf); 797 return -1; 798 } 799 } 800 801 while (bufrem > 0) { 802 i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); 803 if (i == -1) { 804 warn("zeroing image, %lld bytes to go", 805 (long long)bufrem); 806 free(buf); 807 return -1; 808 } 809 bufrem -= i; 810 } 811 if (buf) 812 free(buf); 813 814 /* make the file system */ 815 if (debug & DEBUG_FS_CREATE_IMAGE) 816 APRINTF("calling mkfs(\"%s\", ...)\n", image); 817 hammer2_mkfs(1, av, opt); /* success if returned */ 818 819 return fsopts->fd; 820 } 821 822 static off_t 823 hammer2_phys_size(off_t size) 824 { 825 off_t radix_size, phys_size = 0; 826 int i; 827 828 if (size > HAMMER2_PBUFSIZE) { 829 phys_size += rounddown(size, HAMMER2_PBUFSIZE); 830 size = size % HAMMER2_PBUFSIZE; 831 } 832 833 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) { 834 radix_size = 1UL << i; 835 if (radix_size >= size) { 836 phys_size += radix_size; 837 break; 838 } 839 } 840 841 return phys_size; 842 } 843 844 /* calculate data size */ 845 static void 846 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts) 847 { 848 fsnode *node; 849 850 assert(fsopts != NULL); 851 852 if (debug & DEBUG_FS_SIZE_DIR) 853 APRINTF("entry: bytes %lld inodes %lld\n", 854 (long long)fsopts->size, (long long)fsopts->inodes); 855 856 for (node = root; node != NULL; node = node->next) { 857 if (node == root) { /* we're at "." */ 858 assert(strcmp(node->name, ".") == 0); 859 } else if ((node->inode->flags & FI_SIZED) == 0) { 860 /* don't count duplicate names */ 861 node->inode->flags |= FI_SIZED; 862 if (debug & DEBUG_FS_SIZE_DIR_NODE) 863 APRINTF("`%s' size %lld\n", 864 node->name, 865 (long long)node->inode->st.st_size); 866 fsopts->inodes++; 867 fsopts->size += sizeof(hammer2_inode_data_t); 868 if (node->type == S_IFREG) { 869 size_t st_size = node->inode->st.st_size; 870 if (st_size > HAMMER2_EMBEDDED_BYTES) 871 fsopts->size += hammer2_phys_size(st_size); 872 } else if (node->type == S_IFLNK) { 873 size_t nlen = strlen(node->symlink); 874 if (nlen > HAMMER2_EMBEDDED_BYTES) 875 fsopts->size += hammer2_phys_size(nlen); 876 } 877 } 878 if (node->type == S_IFDIR) 879 hammer2_size_dir(node->child, fsopts); 880 } 881 882 if (debug & DEBUG_FS_SIZE_DIR) 883 APRINTF("exit: size %lld inodes %lld\n", 884 (long long)fsopts->size, (long long)fsopts->inodes); 885 } 886 887 static void 888 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp, 889 const fsnode *node, int depth, const char *msg) 890 { 891 if (debug & DEBUG_FS_POPULATE) { 892 if (1) { 893 int indent = depth * 2; 894 char *type; 895 if (S_ISDIR(node->type)) 896 type = "dir"; 897 else if (S_ISREG(node->type)) 898 type = "reg"; 899 else if (S_ISLNK(node->type)) 900 type = "lnk"; 901 else if (S_ISFIFO(node->type)) 902 type = "fifo"; 903 else 904 type = "???"; 905 printf("%*.*s", indent, indent, ""); 906 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n", 907 dvp, dvp ? VTOI(dvp)->refs : 0, 908 vp, vp ? VTOI(vp)->refs : 0, 909 node->name, type, msg); 910 } else { 911 char type; 912 if (S_ISDIR(node->type)) 913 type = 'd'; 914 else if (S_ISREG(node->type)) 915 type = 'r'; 916 else if (S_ISLNK(node->type)) 917 type = 'l'; 918 else if (S_ISFIFO(node->type)) 919 type = 'f'; 920 else 921 type = '?'; 922 printf("%c", type); 923 fflush(stdout); 924 } 925 } 926 } 927 928 static int 929 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root, 930 fsnode *parent, fsinfo_t *fsopts, int depth) 931 { 932 fsnode *cur; 933 struct m_vnode *vp; 934 struct stat st; 935 char f[MAXPATHLEN]; 936 const char *path; 937 int hardlink; 938 int error; 939 940 assert(dvp != NULL); 941 assert(dir != NULL); 942 assert(root != NULL); 943 assert(parent != NULL); 944 assert(fsopts != NULL); 945 946 /* assert root directory */ 947 assert(S_ISDIR(root->type)); 948 assert(!strcmp(root->name, ".")); 949 assert(!root->child); 950 assert(!root->parent || root->parent->child == root); 951 952 hammer2_print(dvp, NULL, root, depth, "enter"); 953 if (stat(dir, &st) == -1) 954 err(1, "no such path %s", dir); 955 if (!S_ISDIR(st.st_mode)) 956 errx(1, "no such dir %s", dir); 957 958 for (cur = root->next; cur != NULL; cur = cur->next) { 959 /* global variable for HAMMER2 vnops */ 960 hammer2_curnode = cur; 961 962 /* construct source path */ 963 if (cur->contents) { 964 path = cur->contents; 965 } else { 966 if (S_ISDIR(cur->type)) { 967 /* this should be same as root/path/name */ 968 if (snprintf(f, sizeof(f), "%s/%s", 969 dir, cur->name) >= (int)sizeof(f)) 970 errx(1, "path %s too long", f); 971 } else { 972 if (snprintf(f, sizeof(f), "%s/%s/%s", 973 cur->root, cur->path, cur->name) >= (int)sizeof(f)) 974 errx(1, "path %s too long", f); 975 } 976 path = f; 977 } 978 if (stat(path, &st) == -1) 979 err(1, "no such file %s", f); 980 981 /* update node state */ 982 if ((cur->inode->flags & FI_ALLOCATED) == 0) { 983 cur->inode->flags |= FI_ALLOCATED; 984 if (cur != root) 985 cur->parent = parent; 986 } 987 988 /* detect hardlink */ 989 if (cur->inode->flags & FI_WRITTEN) { 990 assert(!S_ISDIR(cur->type)); 991 hardlink = 1; 992 } else { 993 hardlink = 0; 994 } 995 cur->inode->flags |= FI_WRITTEN; 996 997 /* make sure it doesn't exist yet */ 998 vp = NULL; 999 error = hammer2_nresolve(dvp, &vp, cur->name, 1000 strlen(cur->name)); 1001 if (!error) 1002 errx(1, "hammer2_nresolve(\"%s\") already exists", 1003 cur->name); 1004 hammer2_print(dvp, vp, cur, depth, "nresolve"); 1005 1006 /* if directory, mkdir and recurse */ 1007 if (S_ISDIR(cur->type)) { 1008 assert(cur->child); 1009 1010 vp = NULL; 1011 error = hammer2_nmkdir(dvp, &vp, cur->name, 1012 strlen(cur->name), cur->inode->st.st_mode); 1013 if (error) 1014 errx(1, "hammer2_nmkdir(\"%s\") failed: %s", 1015 cur->name, strerror(error)); 1016 assert(vp); 1017 hammer2_print(dvp, vp, cur, depth, "nmkdir"); 1018 1019 error = hammer2_populate_dir(vp, path, cur->child, cur, 1020 fsopts, depth + 1); 1021 if (error) 1022 errx(1, "failed to populate %s: %s", 1023 path, strerror(error)); 1024 cur->inode->param = vp; 1025 continue; 1026 } 1027 1028 /* if regular file, creat and write its data */ 1029 if (S_ISREG(cur->type) && !hardlink) { 1030 assert(cur->child == NULL); 1031 1032 vp = NULL; 1033 error = hammer2_ncreate(dvp, &vp, cur->name, 1034 strlen(cur->name), cur->inode->st.st_mode); 1035 if (error) 1036 errx(1, "hammer2_ncreate(\"%s\") failed: %s", 1037 cur->name, strerror(error)); 1038 assert(vp); 1039 hammer2_print(dvp, vp, cur, depth, "ncreate"); 1040 1041 error = hammer2_write_file(vp, path, cur); 1042 if (error) 1043 errx(1, "hammer2_write_file(\"%s\") failed: %s", 1044 path, strerror(error)); 1045 cur->inode->param = vp; 1046 continue; 1047 } 1048 1049 /* if symlink, create a symlink against target */ 1050 if (S_ISLNK(cur->type)) { 1051 assert(cur->child == NULL); 1052 1053 vp = NULL; 1054 error = hammer2_nsymlink(dvp, &vp, cur->name, 1055 strlen(cur->name), cur->symlink, 1056 cur->inode->st.st_mode); 1057 if (error) 1058 errx(1, "hammer2_nsymlink(\"%s\") failed: %s", 1059 cur->name, strerror(error)); 1060 assert(vp); 1061 hammer2_print(dvp, vp, cur, depth, "nsymlink"); 1062 cur->inode->param = vp; 1063 continue; 1064 } 1065 1066 /* if fifo, create a fifo */ 1067 if (S_ISFIFO(cur->type) && !hardlink) { 1068 assert(cur->child == NULL); 1069 1070 vp = NULL; 1071 error = hammer2_nmknod(dvp, &vp, cur->name, 1072 strlen(cur->name), VFIFO, cur->inode->st.st_mode); 1073 if (error) 1074 errx(1, "hammer2_nmknod(\"%s\") failed: %s", 1075 cur->name, strerror(error)); 1076 assert(vp); 1077 hammer2_print(dvp, vp, cur, depth, "nmknod"); 1078 cur->inode->param = vp; 1079 continue; 1080 } 1081 1082 /* if hardlink, creat a hardlink */ 1083 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) { 1084 char buf[64]; 1085 assert(cur->child == NULL); 1086 1087 /* source vnode must not be NULL */ 1088 vp = cur->inode->param; 1089 assert(vp); 1090 /* currently these conditions must be true */ 1091 assert(vp->v_data); 1092 assert(vp->v_type == VREG || vp->v_type == VFIFO); 1093 assert(vp->v_logical); 1094 assert(!vp->v_vflushed); 1095 assert(vp->v_malloced); 1096 assert(VTOI(vp)->refs > 0); 1097 1098 error = hammer2_nlink(dvp, vp, cur->name, 1099 strlen(cur->name)); 1100 if (error) 1101 errx(1, "hammer2_nlink(\"%s\") failed: %s", 1102 cur->name, strerror(error)); 1103 snprintf(buf, sizeof(buf), "nlink=%lld", 1104 (long long)VTOI(vp)->meta.nlinks); 1105 hammer2_print(dvp, vp, cur, depth, buf); 1106 continue; 1107 } 1108 1109 /* other types are unsupported */ 1110 printf("ignore %s 0%o\n", path, cur->type); 1111 } 1112 1113 return 0; 1114 } 1115 1116 static int 1117 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node) 1118 { 1119 struct stat *st = &node->inode->st; 1120 size_t nsize, bufsize; 1121 off_t offset; 1122 int fd, error; 1123 char *p; 1124 1125 nsize = st->st_size; 1126 if (nsize == 0) 1127 return 0; 1128 /* check nsize vs maximum file size */ 1129 1130 fd = open(path, O_RDONLY); 1131 if (fd < 0) 1132 err(1, "failed to open %s", path); 1133 1134 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 1135 if (p == MAP_FAILED) 1136 err(1, "failed to mmap %s", path); 1137 close(fd); 1138 1139 for (offset = 0; offset < nsize; ) { 1140 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE); 1141 assert(bufsize <= HAMMER2_PBUFSIZE); 1142 error = hammer2_write(vp, p + offset, bufsize, offset); 1143 if (error) 1144 errx(1, "failed to write to %s vnode: %s", 1145 path, strerror(error)); 1146 offset += bufsize; 1147 if (bufsize == HAMMER2_PBUFSIZE) 1148 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0); 1149 } 1150 munmap(p, nsize); 1151 1152 return 0; 1153 } 1154 1155 static int 1156 hammer2_version_get(struct m_vnode *vp) 1157 { 1158 hammer2_dev_t *hmp; 1159 1160 hmp = VTOI(vp)->pmp->pfs_hmps[0]; 1161 if (hmp == NULL) 1162 return EINVAL; 1163 1164 printf("version: %d\n", hmp->voldata.version); 1165 1166 return 0; 1167 } 1168 1169 struct pfs_entry { 1170 TAILQ_ENTRY(pfs_entry) entry; 1171 char name[NAME_MAX+1]; 1172 char s[NAME_MAX+1]; 1173 }; 1174 1175 static int 1176 hammer2_pfs_get(struct m_vnode *vp) 1177 { 1178 hammer2_ioc_pfs_t pfs; 1179 TAILQ_HEAD(, pfs_entry) head; 1180 struct pfs_entry *p, *e; 1181 char *pfs_id_str; 1182 const char *type_str; 1183 int error; 1184 1185 bzero(&pfs, sizeof(pfs)); 1186 TAILQ_INIT(&head); 1187 1188 while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) { 1189 error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs); 1190 if (error) 1191 return error; 1192 1193 pfs_id_str = NULL; 1194 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str); 1195 1196 if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) { 1197 if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE) 1198 type_str = "MASTER"; 1199 else 1200 type_str = hammer2_pfssubtype_to_str( 1201 pfs.pfs_subtype); 1202 } else { 1203 type_str = hammer2_pfstype_to_str(pfs.pfs_type); 1204 } 1205 e = ecalloc(1, sizeof(*e)); 1206 snprintf(e->name, sizeof(e->name), "%s", pfs.name); 1207 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str); 1208 free(pfs_id_str); 1209 1210 p = TAILQ_FIRST(&head); 1211 while (p) { 1212 if (strcmp(e->name, p->name) <= 0) { 1213 TAILQ_INSERT_BEFORE(p, e, entry); 1214 break; 1215 } 1216 p = TAILQ_NEXT(p, entry); 1217 } 1218 if (!p) 1219 TAILQ_INSERT_TAIL(&head, e, entry); 1220 } 1221 1222 printf("Type " 1223 "ClusterId (pfs_clid) " 1224 "Label\n"); 1225 while ((p = TAILQ_FIRST(&head)) != NULL) { 1226 printf("%s %s\n", p->s, p->name); 1227 TAILQ_REMOVE(&head, p, entry); 1228 free(p); 1229 } 1230 1231 return 0; 1232 } 1233 1234 static int 1235 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name) 1236 { 1237 hammer2_ioc_pfs_t pfs; 1238 char *pfs_id_str; 1239 int error; 1240 1241 bzero(&pfs, sizeof(pfs)); 1242 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1243 1244 error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs); 1245 if (error == 0) { 1246 printf("name: %s\n", pfs.name); 1247 printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type)); 1248 printf("subtype: %s\n", 1249 hammer2_pfssubtype_to_str(pfs.pfs_subtype)); 1250 1251 pfs_id_str = NULL; 1252 hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str); 1253 printf("fsid: %s\n", pfs_id_str); 1254 free(pfs_id_str); 1255 1256 pfs_id_str = NULL; 1257 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str); 1258 printf("clid: %s\n", pfs_id_str); 1259 free(pfs_id_str); 1260 } 1261 1262 return error; 1263 } 1264 1265 static int 1266 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name) 1267 { 1268 hammer2_ioc_pfs_t pfs; 1269 int error; 1270 1271 bzero(&pfs, sizeof(pfs)); 1272 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1273 pfs.pfs_type = HAMMER2_PFSTYPE_MASTER; 1274 uuid_create(&pfs.pfs_clid, NULL); 1275 uuid_create(&pfs.pfs_fsid, NULL); 1276 1277 error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs); 1278 if (error == EEXIST) 1279 fprintf(stderr, 1280 "NOTE: Typically the same name is " 1281 "used for cluster elements on " 1282 "different mounts,\n" 1283 " but cluster elements on the " 1284 "same mount require unique names.\n" 1285 "hammer2: pfs_create(%s): already present\n", 1286 pfs_name); 1287 1288 return error; 1289 } 1290 1291 static int 1292 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name) 1293 { 1294 hammer2_ioc_pfs_t pfs; 1295 1296 bzero(&pfs, sizeof(pfs)); 1297 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1298 1299 return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs); 1300 } 1301 1302 static int 1303 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name, 1304 const char *mount_label) 1305 { 1306 hammer2_ioc_pfs_t pfs; 1307 struct tm *tp; 1308 time_t t; 1309 1310 bzero(&pfs, sizeof(pfs)); 1311 strlcpy(pfs.name, pfs_name, sizeof(pfs.name)); 1312 1313 if (strlen(pfs.name) == 0) { 1314 time(&t); 1315 tp = localtime(&t); 1316 snprintf(pfs.name, sizeof(pfs.name), 1317 "%s.%04d%02d%02d.%02d%02d%02d", 1318 mount_label, 1319 tp->tm_year + 1900, 1320 tp->tm_mon + 1, 1321 tp->tm_mday, 1322 tp->tm_hour, 1323 tp->tm_min, 1324 tp->tm_sec); 1325 } 1326 1327 return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs); 1328 } 1329 1330 static int 1331 hammer2_inode_getx(struct m_vnode *dvp, const char *f) 1332 { 1333 hammer2_ioc_inode_t inode; 1334 hammer2_inode_t *ip; 1335 hammer2_inode_meta_t *meta; 1336 struct m_vnode *vp; 1337 char *o, *p, *name, *str = NULL; 1338 int error; 1339 uuid_t uuid; 1340 1341 assert(strlen(f) > 0); 1342 o = p = strdup(f); 1343 1344 /* trim trailing '/' */ 1345 p += strlen(p); 1346 p--; 1347 while (p >= o && *p == '/') 1348 p--; 1349 *++p = 0; 1350 1351 name = p = o; 1352 if (strlen(p) == 0) 1353 return EINVAL; 1354 1355 while ((p = strchr(p, '/')) != NULL) { 1356 *p++ = 0; /* NULL terminate name */ 1357 vp = NULL; 1358 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1359 if (error) 1360 return error; 1361 1362 ip = VTOI(vp); 1363 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY); 1364 1365 dvp = vp; 1366 name = p; 1367 } 1368 1369 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1370 if (error) 1371 return error; 1372 1373 bzero(&inode, sizeof(inode)); 1374 error = hammer2_ioctl_inode_get(VTOI(vp), &inode); 1375 if (error) 1376 return error; 1377 1378 meta = &inode.ip_data.meta; 1379 printf("--------------------\n"); 1380 printf("flags = 0x%x\n", inode.flags); 1381 printf("data_count = %ju\n", (uintmax_t)inode.data_count); 1382 printf("inode_count = %ju\n", (uintmax_t)inode.inode_count); 1383 printf("--------------------\n"); 1384 printf("version = %u\n", meta->version); 1385 printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype, 1386 hammer2_pfssubtype_to_str(meta->pfs_subtype)); 1387 printf("uflags = 0x%x\n", (unsigned int)meta->uflags); 1388 printf("rmajor = %u\n", meta->rmajor); 1389 printf("rminor = %u\n", meta->rminor); 1390 printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str)); 1391 printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str)); 1392 printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str)); 1393 printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str)); 1394 uuid = meta->uid; 1395 printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1396 uuid = meta->gid; 1397 printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1398 printf("type = %u (%s)\n", meta->type, 1399 hammer2_iptype_to_str(meta->type)); 1400 printf("op_flags = 0x%x\n", meta->op_flags); 1401 printf("cap_flags = 0x%x\n", meta->cap_flags); 1402 printf("mode = 0%o\n", meta->mode); 1403 printf("inum = 0x%jx\n", (uintmax_t)meta->inum); 1404 printf("size = %ju\n", (uintmax_t)meta->size); 1405 printf("nlinks = %ju\n", (uintmax_t)meta->nlinks); 1406 printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent); 1407 printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key); 1408 printf("name_len = %u\n", meta->name_len); 1409 printf("ncopies = %u\n", meta->ncopies); 1410 printf("comp_algo = %u\n", meta->comp_algo); 1411 printf("target_type = %u\n", meta->target_type); 1412 printf("check_algo = %u\n", meta->check_algo); 1413 printf("pfs_nmasters = %u\n", meta->pfs_nmasters); 1414 printf("pfs_type = %u (%s)\n", meta->pfs_type, 1415 hammer2_pfstype_to_str(meta->pfs_type)); 1416 printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum); 1417 uuid = meta->pfs_clid; 1418 printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1419 uuid = meta->pfs_fsid; 1420 printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str)); 1421 printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota); 1422 printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota); 1423 printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid); 1424 printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check); 1425 printf("--------------------\n"); 1426 1427 free(o); 1428 1429 return error; 1430 } 1431 1432 static int 1433 hammer2_bulkfree(struct m_vnode *vp) 1434 { 1435 hammer2_ioc_bulkfree_t bfi; 1436 size_t usermem; 1437 size_t usermem_size = sizeof(usermem); 1438 1439 bzero(&bfi, sizeof(bfi)); 1440 usermem = 0; 1441 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0) 1442 bfi.size = usermem / 16; 1443 else 1444 bfi.size = 0; 1445 if (bfi.size < 8192 * 1024) 1446 bfi.size = 8192 * 1024; 1447 1448 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi); 1449 } 1450 1451 static int 1452 hammer2_destroy_path(struct m_vnode *dvp, const char *f) 1453 { 1454 hammer2_ioc_destroy_t destroy; 1455 hammer2_inode_t *ip; 1456 struct m_vnode *vp; 1457 char *o, *p, *name; 1458 int error; 1459 1460 assert(strlen(f) > 0); 1461 o = p = strdup(f); 1462 1463 /* trim trailing '/' */ 1464 p += strlen(p); 1465 p--; 1466 while (p >= o && *p == '/') 1467 p--; 1468 *++p = 0; 1469 1470 name = p = o; 1471 if (strlen(p) == 0) 1472 return EINVAL; 1473 1474 while ((p = strchr(p, '/')) != NULL) { 1475 *p++ = 0; /* NULL terminate name */ 1476 vp = NULL; 1477 error = hammer2_nresolve(dvp, &vp, name, strlen(name)); 1478 if (error) 1479 return error; 1480 1481 ip = VTOI(vp); 1482 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY); 1483 /* XXX When does (or why does not) ioctl modify this inode ? */ 1484 hammer2_inode_modify(ip); 1485 1486 dvp = vp; 1487 name = p; 1488 } 1489 1490 bzero(&destroy, sizeof(destroy)); 1491 destroy.cmd = HAMMER2_DELETE_FILE; 1492 snprintf(destroy.path, sizeof(destroy.path), "%s", name); 1493 1494 printf("%s\t", f); 1495 fflush(stdout); 1496 1497 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy); 1498 if (error) 1499 printf("%s\n", strerror(error)); 1500 else 1501 printf("ok\n"); 1502 free(o); 1503 1504 return error; 1505 } 1506 1507 static int 1508 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum) 1509 { 1510 hammer2_ioc_destroy_t destroy; 1511 int error; 1512 1513 bzero(&destroy, sizeof(destroy)); 1514 destroy.cmd = HAMMER2_DELETE_INUM; 1515 destroy.inum = inum; 1516 1517 printf("%jd\t", (intmax_t)destroy.inum); 1518 fflush(stdout); 1519 1520 error = hammer2_ioctl_destroy(VTOI(vp), &destroy); 1521 if (error) 1522 printf("%s\n", strerror(error)); 1523 else 1524 printf("ok\n"); 1525 1526 return error; 1527 } 1528 1529 static int 1530 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size) 1531 { 1532 hammer2_ioc_growfs_t growfs; 1533 int error; 1534 1535 bzero(&growfs, sizeof(growfs)); 1536 growfs.size = size; 1537 1538 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL); 1539 if (!error) { 1540 if (growfs.modified) 1541 printf("grown to %016jx\n", (intmax_t)growfs.size); 1542 else 1543 printf("no size change - %016jx\n", 1544 (intmax_t)growfs.size); 1545 } 1546 1547 return error; 1548 } 1549