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