1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 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 * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.12 2008/10/08 21:01:54 thomas Exp $ 35 */ 36 37 #include <libgen.h> 38 39 #include "hammer.h" 40 41 static int scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path); 42 static void parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd); 43 static void init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave); 44 static void pseudofs_usage(int code); 45 static int getyn(void); 46 static int timetosecs(char *str); 47 48 /* 49 * Return a directory that contains path. 50 * If '/' is not found in the path then '.' is returned. 51 * A caller need to free the returned pointer. 52 */ 53 static char* 54 getdir(const char *path) 55 { 56 char *dirpath; 57 58 dirpath = strdup(path); 59 if (strrchr(dirpath, '/')) { 60 *strrchr(dirpath, '/') = 0; 61 if (strlen(dirpath) == 0) { 62 free(dirpath); 63 dirpath = strdup("/"); 64 } 65 } else { 66 free(dirpath); 67 dirpath = strdup("."); 68 } 69 70 return(dirpath); 71 } 72 73 /* 74 * Calculate the pfs_id given a path to a directory or a @@PFS or @@%llx:%d 75 * softlink. 76 */ 77 int 78 getpfs(struct hammer_ioc_pseudofs_rw *pfs, char *path) 79 { 80 int fd; 81 char *p; 82 83 bzero(pfs, sizeof(*pfs)); 84 pfs->ondisk = malloc(sizeof(*pfs->ondisk)); 85 bzero(pfs->ondisk, sizeof(*pfs->ondisk)); 86 pfs->bytes = sizeof(*pfs->ondisk); 87 88 /* 89 * Trailing '/' must be removed so that upon pfs-destroy 90 * the symlink can be deleted without problems. 91 * Root directory (/) must be excluded from this. 92 */ 93 p = path + (int)strlen(path) - 1; 94 assert(p >= path); 95 while (p != path && *p == '/') 96 *p-- = 0; 97 98 fd = scanpfsid(pfs, path); 99 if (fd < 0) { 100 /* 101 * Once it comes here the hammer command may fail even if 102 * this function returns valid file descriptor. 103 */ 104 fd = open(path, O_RDONLY); 105 if (fd >= 0) { 106 pfs->pfs_id = -1; 107 ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs); 108 if (pfs->pfs_id == -1) { 109 close(fd); 110 fd = -1; 111 } 112 } 113 } 114 115 if (fd < 0) { 116 fprintf(stderr, "Cannot access PFS %s: %s\n", 117 path, strerror(errno)); 118 exit(1); 119 } 120 121 /* 122 * pfs.pfs_id should have been set to non -1. In this case fd 123 * could be any fd of HAMMER inodes since HAMMERIOC_GET_PSEUDOFS 124 * doesn't depend on inode attributes if it's set to a valid id. 125 */ 126 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs) < 0) { 127 fprintf(stderr, "Cannot access PFS %s: %s\n", 128 path, strerror(errno)); 129 exit(1); 130 } 131 return(fd); 132 } 133 134 /* 135 * Extract the PFS id from path. Return a file descriptor of 136 * the parent directory which is expected to be located under 137 * the root filesystem (not another PFS). 138 */ 139 static int 140 scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path) 141 { 142 int fd; 143 int n; 144 const char *p; 145 char *dirpath; 146 char buf[64]; 147 uintmax_t dummy_tid; 148 struct stat st; 149 150 if (stat(path, &st)) { 151 /* possibly slave PFS */ 152 } else if (S_ISDIR(st.st_mode)) { 153 /* possibly master or slave PFS */ 154 } else { 155 return -1; /* neither */ 156 } 157 158 /* 159 * If the path is a link read the link. 160 */ 161 if (lstat(path, &st) == 0 && S_ISLNK(st.st_mode)) { 162 n = readlink(path, buf, sizeof(buf) - 1); 163 if (n < 0) 164 n = 0; 165 buf[n] = 0; 166 p = buf; 167 } else { 168 p = path; 169 } 170 171 /* 172 * The symlink created by pfs-master|slave is just a symlink. 173 * One could happen to remove a symlink and relink PFS as 174 * # ln -s ./@@-1:00001 ./link 175 * which results PFS having something extra before @@. 176 * One could also directly use the PFS and results the same. 177 * Get rid of it before we extract the PFS id. 178 */ 179 if (strchr(p, '/')) { 180 p = basename(p); 181 if (p == NULL) 182 err(1, "basename"); 183 } 184 185 /* 186 * Extract the PFS from the link. HAMMER will automatically 187 * convert @@PFS%05d links so if actually see one in that 188 * form the target PFS may not exist or may be corrupt. But 189 * we can extract the PFS id anyway. 190 */ 191 dirpath = getdir(path); 192 if (sscanf(p, "@@PFS%d", &pfs->pfs_id) == 1) { 193 fd = open(dirpath, O_RDONLY); 194 } else if (sscanf(p, "@@%jx:%d", &dummy_tid, &pfs->pfs_id) == 2) { 195 fd = open(dirpath, O_RDONLY); 196 } else { 197 fd = -1; /* Failed to scan PFS id */ 198 } 199 free(dirpath); 200 return(fd); 201 } 202 203 void 204 relpfs(int fd, struct hammer_ioc_pseudofs_rw *pfs) 205 { 206 if (fd >= 0) 207 close(fd); 208 if (pfs->ondisk) { 209 free(pfs->ondisk); 210 pfs->ondisk = NULL; 211 } 212 } 213 214 void 215 hammer_cmd_pseudofs_status(char **av, int ac) 216 { 217 struct hammer_ioc_pseudofs_rw pfs; 218 int i; 219 int fd; 220 221 if (ac == 0) 222 pseudofs_usage(1); 223 224 for (i = 0; i < ac; ++i) { 225 fd = getpfs(&pfs, av[i]); 226 printf("%s\t", av[i]); 227 if (fd < 0 || ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 228 printf("Invalid PFS path %s\n", av[i]); 229 } else { 230 printf("PFS #%d {\n", pfs.pfs_id); 231 dump_pfsd(pfs.ondisk, fd); 232 printf("}\n"); 233 } 234 if (fd >= 0) 235 close(fd); 236 if (pfs.ondisk) 237 free(pfs.ondisk); 238 relpfs(fd, &pfs); 239 } 240 } 241 242 void 243 hammer_cmd_pseudofs_create(char **av, int ac, int is_slave) 244 { 245 struct hammer_ioc_pseudofs_rw pfs; 246 struct hammer_pseudofs_data pfsd; 247 struct stat st; 248 const char *path; 249 char *dirpath; 250 char *linkpath; 251 int pfs_id; 252 int fd; 253 int error; 254 255 if (ac == 0) 256 pseudofs_usage(1); 257 path = av[0]; 258 if (lstat(path, &st) == 0) { 259 fprintf(stderr, "Cannot create %s, file exists!\n", path); 260 exit(1); 261 } 262 263 /* 264 * Figure out the directory prefix, taking care of degenerate 265 * cases. 266 */ 267 dirpath = getdir(path); 268 fd = open(dirpath, O_RDONLY); 269 if (fd < 0) { 270 fprintf(stderr, "Cannot open directory %s\n", dirpath); 271 exit(1); 272 } 273 274 /* 275 * Avoid foot-shooting. Don't let the user create a PFS 276 * softlink via a PFS. PFS softlinks may only be accessed 277 * via the master filesystem. Checking it here ensures 278 * other PFS commands access PFS under the master filesystem. 279 */ 280 bzero(&pfs, sizeof(pfs)); 281 bzero(&pfsd, sizeof(pfsd)); 282 pfs.pfs_id = -1; 283 pfs.ondisk = &pfsd; 284 pfs.bytes = sizeof(pfsd); 285 286 ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs); 287 if (pfs.pfs_id != HAMMER_ROOT_PFSID) { 288 fprintf(stderr, 289 "You are attempting to access a PFS softlink " 290 "from a PFS. It may not represent the PFS\n" 291 "on the main filesystem mount that you " 292 "expect! You may only access PFS softlinks\n" 293 "via the main filesystem mount!\n"); 294 exit(1); 295 } 296 297 error = 0; 298 for (pfs_id = 0; pfs_id < HAMMER_MAX_PFS; ++pfs_id) { 299 bzero(&pfs, sizeof(pfs)); 300 bzero(&pfsd, sizeof(pfsd)); 301 pfs.pfs_id = pfs_id; 302 pfs.ondisk = &pfsd; 303 pfs.bytes = sizeof(pfsd); 304 pfs.version = HAMMER_IOC_PSEUDOFS_VERSION; 305 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 306 error = errno; 307 break; 308 } 309 } 310 if (pfs_id == HAMMER_MAX_PFS) { 311 fprintf(stderr, "Cannot create %s, all PFSs in use\n", path); 312 exit(1); 313 } else if (pfs_id == HAMMER_ROOT_PFSID) { 314 fprintf(stderr, "Fatal error: PFS#%d must exist\n", 315 HAMMER_ROOT_PFSID); 316 exit(1); 317 } 318 319 if (error != ENOENT) { 320 fprintf(stderr, "Cannot create %s, got %s during scan\n", 321 path, strerror(error)); 322 exit(1); 323 } 324 325 /* 326 * Create the new PFS 327 */ 328 printf("Creating PFS #%d\t", pfs_id); 329 init_pfsd(&pfsd, is_slave); 330 pfs.pfs_id = pfs_id; 331 pfs.ondisk = &pfsd; 332 pfs.bytes = sizeof(pfsd); 333 pfs.version = HAMMER_IOC_PSEUDOFS_VERSION; 334 335 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 336 printf("failed: %s\n", strerror(errno)); 337 } else { 338 /* special symlink, must be exactly 10 characters */ 339 asprintf(&linkpath, "@@PFS%05d", pfs_id); 340 if (symlink(linkpath, path) < 0) { 341 printf("failed: cannot create symlink: %s\n", 342 strerror(errno)); 343 } else { 344 printf("succeeded!\n"); 345 hammer_cmd_pseudofs_update(av, ac); 346 } 347 } 348 free(dirpath); 349 close(fd); 350 } 351 352 void 353 hammer_cmd_pseudofs_destroy(char **av, int ac) 354 { 355 struct hammer_ioc_pseudofs_rw pfs; 356 struct hammer_pseudofs_data pfsd; 357 struct stat st; 358 int fd; 359 int i; 360 361 if (ac == 0) 362 pseudofs_usage(1); 363 fd = getpfs(&pfs, av[0]); 364 365 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 366 fprintf(stderr, "You cannot destroy PFS#0\n"); 367 exit(1); 368 } 369 printf("You have requested that PFS#%d (%s) be destroyed\n", 370 pfs.pfs_id, pfs.ondisk->label); 371 printf("This will irrevocably destroy all data on this PFS!!!!!\n"); 372 printf("Do you really want to do this? "); 373 fflush(stdout); 374 if (getyn() == 0) { 375 fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); 376 exit(1); 377 } 378 379 if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 380 printf("This PFS is currently setup as a MASTER!\n"); 381 printf("Are you absolutely sure you want to destroy it? "); 382 fflush(stdout); 383 if (getyn() == 0) { 384 fprintf(stderr, "No action taken on PFS#%d\n", 385 pfs.pfs_id); 386 exit(1); 387 } 388 } 389 390 printf("Destroying PFS #%d (%s) in ", pfs.pfs_id, pfs.ondisk->label); 391 for (i = 5; i; --i) { 392 printf(" %d", i); 393 fflush(stdout); 394 sleep(1); 395 } 396 printf(".. starting destruction pass\n"); 397 fflush(stdout); 398 399 /* 400 * Save the original PFS data so we can restore if on failure. 401 * It fails to destroy, for example, if the PFS is still mounted. 402 */ 403 bcopy(pfs.ondisk, &pfsd, sizeof(pfsd)); 404 405 /* 406 * Set the sync_beg_tid and sync_end_tid's to 1, once we start the 407 * RMR the PFS is basically destroyed even if someone ^C's it. 408 */ 409 pfs.ondisk->mirror_flags |= HAMMER_PFSD_SLAVE; 410 pfs.ondisk->reserved01 = -1; 411 pfs.ondisk->sync_beg_tid = 1; 412 pfs.ondisk->sync_end_tid = 1; 413 414 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 415 fprintf(stderr, "Unable to update the PFS configuration: %s\n", 416 strerror(errno)); 417 exit(1); 418 } 419 420 /* 421 * Ok, do it. Remove the softlink on success. 422 */ 423 if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) { 424 printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id); 425 if (lstat(av[0], &st) == 0 && S_ISLNK(st.st_mode)) { 426 if (remove(av[0]) < 0) { 427 fprintf(stderr, "Unable to remove softlink: %s " 428 "(but the PFS has been destroyed)\n", 429 av[0]); 430 /* exit status 0 anyway */ 431 } 432 } 433 } else { 434 printf("pfs-destroy of PFS#%d failed: %s\n", 435 pfs.pfs_id, strerror(errno)); 436 /* 437 * Restore the pfsd as we don't want to keep it downgraded. 438 * This simply restores ondisk PFS with the original data 439 * without creating a new root inode as it already exists. 440 */ 441 bcopy(&pfsd, pfs.ondisk, sizeof(pfsd)); 442 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 443 printf("Failed to restore the original PFS#%d data\n", 444 pfs.pfs_id); 445 exit(1); 446 } 447 } 448 relpfs(fd, &pfs); 449 } 450 451 void 452 hammer_cmd_pseudofs_upgrade(char **av, int ac) 453 { 454 struct hammer_ioc_pseudofs_rw pfs; 455 int fd; 456 457 if (ac == 0) 458 pseudofs_usage(1); 459 fd = getpfs(&pfs, av[0]); 460 461 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 462 fprintf(stderr, "You cannot upgrade PFS#0" 463 " (It should already be a master)\n"); 464 exit(1); 465 } else if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 466 printf("It is already a master\n"); 467 exit(1); 468 } 469 470 if (ioctl(fd, HAMMERIOC_UPG_PSEUDOFS, &pfs) == 0) { 471 printf("pfs-upgrade of PFS#%d (%s) succeeded\n", 472 pfs.pfs_id, pfs.ondisk->label); 473 } else { 474 fprintf(stderr, "pfs-upgrade of PFS#%d (%s) failed: %s\n", 475 pfs.pfs_id, pfs.ondisk->label, strerror(errno)); 476 } 477 relpfs(fd, &pfs); 478 } 479 480 void 481 hammer_cmd_pseudofs_downgrade(char **av, int ac) 482 { 483 struct hammer_ioc_pseudofs_rw pfs; 484 int fd; 485 486 if (ac == 0) 487 pseudofs_usage(1); 488 fd = getpfs(&pfs, av[0]); 489 490 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 491 fprintf(stderr, "You cannot downgrade PFS#0\n"); 492 exit(1); 493 } else if (pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) { 494 printf("It is already a slave\n"); 495 exit(1); 496 } 497 498 if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) { 499 printf("pfs-downgrade of PFS#%d (%s) succeeded\n", 500 pfs.pfs_id, pfs.ondisk->label); 501 } else { 502 fprintf(stderr, "pfs-downgrade of PFS#%d (%s) failed: %s\n", 503 pfs.pfs_id, pfs.ondisk->label, strerror(errno)); 504 } 505 relpfs(fd, &pfs); 506 } 507 508 void 509 hammer_cmd_pseudofs_update(char **av, int ac) 510 { 511 struct hammer_ioc_pseudofs_rw pfs; 512 int fd; 513 514 if (ac == 0) 515 pseudofs_usage(1); 516 fd = getpfs(&pfs, av[0]); 517 518 printf("%s\n", av[0]); 519 fflush(stdout); 520 521 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 522 parse_pfsd_options(av + 1, ac - 1, pfs.ondisk); 523 if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) && 524 pfs.pfs_id == HAMMER_ROOT_PFSID) { 525 printf("The real mount point cannot be made a PFS " 526 "slave, only PFS sub-directories can be made " 527 "slaves\n"); 528 exit(1); 529 } 530 pfs.bytes = sizeof(*pfs.ondisk); 531 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) { 532 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 533 dump_pfsd(pfs.ondisk, fd); 534 } else { 535 printf("Unable to retrieve pfs configuration " 536 "after successful update: %s\n", 537 strerror(errno)); 538 exit(1); 539 } 540 } else { 541 printf("Unable to adjust pfs configuration: %s\n", 542 strerror(errno)); 543 exit(1); 544 } 545 } 546 relpfs(fd, &pfs); 547 } 548 549 static void 550 init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave) 551 { 552 uint32_t status; 553 554 bzero(pfsd, sizeof(*pfsd)); 555 pfsd->sync_beg_tid = 1; 556 pfsd->sync_end_tid = 1; 557 pfsd->sync_beg_ts = 0; 558 pfsd->sync_end_ts = 0; 559 uuid_create(&pfsd->shared_uuid, &status); 560 uuid_create(&pfsd->unique_uuid, &status); 561 if (is_slave) 562 pfsd->mirror_flags |= HAMMER_PFSD_SLAVE; 563 } 564 565 void 566 dump_pfsd(hammer_pseudofs_data_t pfsd, int fd) 567 { 568 struct hammer_ioc_version version; 569 uint32_t status; 570 char *str = NULL; 571 572 printf(" sync-beg-tid=0x%016jx\n", (uintmax_t)pfsd->sync_beg_tid); 573 printf(" sync-end-tid=0x%016jx\n", (uintmax_t)pfsd->sync_end_tid); 574 uuid_to_string(&pfsd->shared_uuid, &str, &status); 575 printf(" shared-uuid=%s\n", str); 576 free(str); 577 uuid_to_string(&pfsd->unique_uuid, &str, &status); 578 printf(" unique-uuid=%s\n", str); 579 free(str); 580 printf(" label=\"%s\"\n", pfsd->label); 581 if (pfsd->snapshots[0]) 582 printf(" snapshots=\"%s\"\n", pfsd->snapshots); 583 if (pfsd->prune_min < (60 * 60 * 24)) { 584 printf(" prune-min=%02d:%02d:%02d\n", 585 pfsd->prune_min / 60 / 60 % 24, 586 pfsd->prune_min / 60 % 60, 587 pfsd->prune_min % 60); 588 } else if (pfsd->prune_min % (60 * 60 * 24)) { 589 printf(" prune-min=%dd/%02d:%02d:%02d\n", 590 pfsd->prune_min / 60 / 60 / 24, 591 pfsd->prune_min / 60 / 60 % 24, 592 pfsd->prune_min / 60 % 60, 593 pfsd->prune_min % 60); 594 } else { 595 printf(" prune-min=%dd\n", pfsd->prune_min / 60 / 60 / 24); 596 } 597 598 if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) { 599 printf(" operating as a SLAVE\n"); 600 } else { 601 printf(" operating as a MASTER\n"); 602 } 603 604 /* 605 * Snapshots directory cannot be shown when there is no fd since 606 * hammer version can't be retrieved. mirror-dump passes -1 because 607 * its input came from mirror-read output thus no path is available 608 * to open(2). 609 */ 610 if (fd >= 0 && pfsd->snapshots[0] == 0) { 611 bzero(&version, sizeof(version)); 612 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) 613 return; 614 if (version.cur_version < 3) { 615 if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) { 616 printf(" snapshots directory not set for " 617 "slave\n"); 618 } else { 619 printf(" snapshots directory for master " 620 "defaults to <pfs>/snapshots\n"); 621 } 622 } else { 623 printf(" snapshots directory defaults to " 624 "/var/hammer/<pfs>\n"); 625 } 626 } 627 } 628 629 static void 630 parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd) 631 { 632 char *cmd; 633 char *ptr; 634 int len; 635 uint32_t status; 636 637 while (ac) { 638 cmd = *av; 639 if ((ptr = strchr(cmd, '=')) != NULL) 640 *ptr++ = 0; 641 642 /* 643 * Basic assignment value test 644 */ 645 if (ptr == NULL) { 646 fprintf(stderr, 647 "option %s requires an assignment\n", 648 cmd); 649 exit(1); 650 } 651 652 status = uuid_s_ok; 653 if (strcmp(cmd, "sync-beg-tid") == 0) { 654 pfsd->sync_beg_tid = strtoull(ptr, NULL, 16); 655 } else if (strcmp(cmd, "sync-end-tid") == 0) { 656 pfsd->sync_end_tid = strtoull(ptr, NULL, 16); 657 } else if (strcmp(cmd, "shared-uuid") == 0) { 658 uuid_from_string(ptr, &pfsd->shared_uuid, &status); 659 } else if (strcmp(cmd, "unique-uuid") == 0) { 660 uuid_from_string(ptr, &pfsd->unique_uuid, &status); 661 } else if (strcmp(cmd, "label") == 0) { 662 len = strlen(ptr); 663 if (ptr[0] == '"' && ptr[len-1] == '"') { 664 ptr[len-1] = 0; 665 ++ptr; 666 } else if (ptr[0] == '"') { 667 fprintf(stderr, 668 "option %s: malformed string\n", 669 cmd); 670 exit(1); 671 } 672 snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr); 673 } else if (strcmp(cmd, "snapshots") == 0) { 674 len = strlen(ptr); 675 if (ptr[0] != '/') { 676 fprintf(stderr, 677 "option %s: '%s' must be an " 678 "absolute path\n", cmd, ptr); 679 if (ptr[0] == 0) { 680 fprintf(stderr, 681 "use 'snapshots-clear' " 682 "to unset snapshots dir\n"); 683 } 684 exit(1); 685 } 686 if (len >= (int)sizeof(pfsd->snapshots)) { 687 fprintf(stderr, 688 "option %s: path too long, %d " 689 "character limit\n", cmd, len); 690 exit(1); 691 } 692 snprintf(pfsd->snapshots, sizeof(pfsd->snapshots), 693 "%s", ptr); 694 } else if (strcmp(cmd, "snapshots-clear") == 0) { 695 pfsd->snapshots[0] = 0; 696 } else if (strcmp(cmd, "prune-min") == 0) { 697 pfsd->prune_min = timetosecs(ptr); 698 if (pfsd->prune_min < 0) { 699 fprintf(stderr, 700 "option %s: illegal time spec, " 701 "use Nd or [Nd/]hh[:mm[:ss]]\n", ptr); 702 exit(1); 703 } 704 } else { 705 fprintf(stderr, "invalid option: %s\n", cmd); 706 exit(1); 707 } 708 if (status != uuid_s_ok) { 709 fprintf(stderr, "option %s: error parsing uuid %s\n", 710 cmd, ptr); 711 exit(1); 712 } 713 --ac; 714 ++av; 715 } 716 } 717 718 static 719 void 720 pseudofs_usage(int code) 721 { 722 fprintf(stderr, 723 "hammer pfs-status <dirpath> ...\n" 724 "hammer pfs-master <dirpath> [options]\n" 725 "hammer pfs-slave <dirpath> [options]\n" 726 "hammer pfs-update <dirpath> [options]\n" 727 "hammer pfs-upgrade <dirpath>\n" 728 "hammer pfs-downgrade <dirpath>\n" 729 "hammer pfs-destroy <dirpath>\n" 730 "\n" 731 "options:\n" 732 " sync-beg-tid=0x16llx\n" 733 " sync-end-tid=0x16llx\n" 734 " shared-uuid=0x16llx\n" 735 " unique-uuid=0x16llx\n" 736 " label=\"string\"\n" 737 " snapshots=\"/path\"\n" 738 " snapshots-clear\n" 739 " prune-min=Nd\n" 740 " prune-min=[Nd/]hh[:mm[:ss]]\n" 741 ); 742 exit(code); 743 } 744 745 static 746 int 747 getyn(void) 748 { 749 char buf[256]; 750 int len; 751 752 if (fgets(buf, sizeof(buf), stdin) == NULL) 753 return(0); 754 len = strlen(buf); 755 while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) 756 --len; 757 buf[len] = 0; 758 if (strcmp(buf, "y") == 0 || 759 strcmp(buf, "yes") == 0 || 760 strcmp(buf, "Y") == 0 || 761 strcmp(buf, "YES") == 0) { 762 return(1); 763 } 764 return(0); 765 } 766 767 /* 768 * Convert time in the form [Nd/]hh[:mm[:ss]] to seconds. 769 * 770 * Return -1 if a parse error occurs. 771 * Return 0x7FFFFFFF if the time exceeds the maximum allowed. 772 */ 773 static 774 int 775 timetosecs(char *str) 776 { 777 int days = 0; 778 int hrs = 0; 779 int mins = 0; 780 int secs = 0; 781 int n; 782 long long v; 783 char *ptr; 784 785 n = strtol(str, &ptr, 10); 786 if (n < 0) 787 return(-1); 788 if (*ptr == 'd') { 789 days = n; 790 ++ptr; 791 if (*ptr == '/') 792 n = strtol(ptr + 1, &ptr, 10); 793 else 794 n = 0; 795 } 796 if (n < 0) 797 return(-1); 798 hrs = n; 799 if (*ptr == ':') { 800 n = strtol(ptr + 1, &ptr, 10); 801 if (n < 0) 802 return(-1); 803 mins = n; 804 if (*ptr == ':') { 805 n = strtol(ptr + 1, &ptr, 10); 806 if (n < 0) 807 return(-1); 808 secs = n; 809 } 810 } 811 if (*ptr) 812 return(-1); 813 v = days * 24 * 60 * 60 + hrs * 60 * 60 + mins * 60 + secs; 814 if (v > 0x7FFFFFFF) 815 v = 0x7FFFFFFF; 816 return((int)v); 817 } 818