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 != 0) { 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 } 314 if (error != ENOENT) { 315 fprintf(stderr, "Cannot create %s, got %s during scan\n", 316 path, strerror(error)); 317 exit(1); 318 } 319 320 /* 321 * Create the new PFS 322 */ 323 printf("Creating PFS #%d\t", pfs_id); 324 init_pfsd(&pfsd, is_slave); 325 pfs.pfs_id = pfs_id; 326 pfs.ondisk = &pfsd; 327 pfs.bytes = sizeof(pfsd); 328 pfs.version = HAMMER_IOC_PSEUDOFS_VERSION; 329 330 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 331 printf("failed: %s\n", strerror(errno)); 332 } else { 333 /* special symlink, must be exactly 10 characters */ 334 asprintf(&linkpath, "@@PFS%05d", pfs_id); 335 if (symlink(linkpath, path) < 0) { 336 printf("failed: cannot create symlink: %s\n", 337 strerror(errno)); 338 } else { 339 printf("succeeded!\n"); 340 hammer_cmd_pseudofs_update(av, ac); 341 } 342 } 343 free(dirpath); 344 close(fd); 345 } 346 347 void 348 hammer_cmd_pseudofs_destroy(char **av, int ac) 349 { 350 struct hammer_ioc_pseudofs_rw pfs; 351 struct hammer_pseudofs_data pfsd; 352 struct stat st; 353 int fd; 354 int i; 355 356 if (ac == 0) 357 pseudofs_usage(1); 358 fd = getpfs(&pfs, av[0]); 359 360 if (pfs.pfs_id == 0) { 361 fprintf(stderr, "You cannot destroy PFS#0\n"); 362 exit(1); 363 } 364 printf("You have requested that PFS#%d (%s) be destroyed\n", 365 pfs.pfs_id, pfs.ondisk->label); 366 printf("This will irrevocably destroy all data on this PFS!!!!!\n"); 367 printf("Do you really want to do this? "); 368 fflush(stdout); 369 if (getyn() == 0) { 370 fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); 371 exit(1); 372 } 373 374 if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 375 printf("This PFS is currently setup as a MASTER!\n"); 376 printf("Are you absolutely sure you want to destroy it? "); 377 fflush(stdout); 378 if (getyn() == 0) { 379 fprintf(stderr, "No action taken on PFS#%d\n", 380 pfs.pfs_id); 381 exit(1); 382 } 383 } 384 385 printf("Destroying PFS #%d (%s) in ", pfs.pfs_id, pfs.ondisk->label); 386 for (i = 5; i; --i) { 387 printf(" %d", i); 388 fflush(stdout); 389 sleep(1); 390 } 391 printf(".. starting destruction pass\n"); 392 fflush(stdout); 393 394 /* 395 * Save the original PFS data so we can restore if on failure. 396 * It fails to destroy, for example, if the PFS is still mounted. 397 */ 398 bcopy(pfs.ondisk, &pfsd, sizeof(pfsd)); 399 400 /* 401 * Set the sync_beg_tid and sync_end_tid's to 1, once we start the 402 * RMR the PFS is basically destroyed even if someone ^C's it. 403 */ 404 pfs.ondisk->mirror_flags |= HAMMER_PFSD_SLAVE; 405 pfs.ondisk->reserved01 = -1; 406 pfs.ondisk->sync_beg_tid = 1; 407 pfs.ondisk->sync_end_tid = 1; 408 409 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 410 fprintf(stderr, "Unable to update the PFS configuration: %s\n", 411 strerror(errno)); 412 exit(1); 413 } 414 415 /* 416 * Ok, do it. Remove the softlink on success. 417 */ 418 if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) { 419 printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id); 420 if (lstat(av[0], &st) == 0 && S_ISLNK(st.st_mode)) { 421 if (remove(av[0]) < 0) { 422 fprintf(stderr, "Unable to remove softlink: %s " 423 "(but the PFS has been destroyed)\n", 424 av[0]); 425 /* exit status 0 anyway */ 426 } 427 } 428 } else { 429 printf("pfs-destroy of PFS#%d failed: %s\n", 430 pfs.pfs_id, strerror(errno)); 431 /* 432 * Restore the pfsd as we don't want to keep it downgraded. 433 * This simply restores ondisk PFS with the original data 434 * without creating a new root inode as it already exists. 435 */ 436 bcopy(&pfsd, pfs.ondisk, sizeof(pfsd)); 437 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 438 printf("Failed to restore the original PFS#%d data\n", 439 pfs.pfs_id); 440 exit(1); 441 } 442 } 443 relpfs(fd, &pfs); 444 } 445 446 void 447 hammer_cmd_pseudofs_upgrade(char **av, int ac) 448 { 449 struct hammer_ioc_pseudofs_rw pfs; 450 int fd; 451 452 if (ac == 0) 453 pseudofs_usage(1); 454 fd = getpfs(&pfs, av[0]); 455 456 if (pfs.pfs_id == 0) { 457 fprintf(stderr, "You cannot upgrade PFS#0" 458 " (It should already be a master)\n"); 459 exit(1); 460 } else if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 461 printf("It is already a master\n"); 462 exit(1); 463 } 464 465 if (ioctl(fd, HAMMERIOC_UPG_PSEUDOFS, &pfs) == 0) { 466 printf("pfs-upgrade of PFS#%d (%s) succeeded\n", 467 pfs.pfs_id, pfs.ondisk->label); 468 } else { 469 fprintf(stderr, "pfs-upgrade of PFS#%d (%s) failed: %s\n", 470 pfs.pfs_id, pfs.ondisk->label, strerror(errno)); 471 } 472 relpfs(fd, &pfs); 473 } 474 475 void 476 hammer_cmd_pseudofs_downgrade(char **av, int ac) 477 { 478 struct hammer_ioc_pseudofs_rw pfs; 479 int fd; 480 481 if (ac == 0) 482 pseudofs_usage(1); 483 fd = getpfs(&pfs, av[0]); 484 485 if (pfs.pfs_id == 0) { 486 fprintf(stderr, "You cannot downgrade PFS#0\n"); 487 exit(1); 488 } else if (pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) { 489 printf("It is already a slave\n"); 490 exit(1); 491 } 492 493 if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) { 494 printf("pfs-downgrade of PFS#%d (%s) succeeded\n", 495 pfs.pfs_id, pfs.ondisk->label); 496 } else { 497 fprintf(stderr, "pfs-downgrade of PFS#%d (%s) failed: %s\n", 498 pfs.pfs_id, pfs.ondisk->label, strerror(errno)); 499 } 500 relpfs(fd, &pfs); 501 } 502 503 void 504 hammer_cmd_pseudofs_update(char **av, int ac) 505 { 506 struct hammer_ioc_pseudofs_rw pfs; 507 int fd; 508 509 if (ac == 0) 510 pseudofs_usage(1); 511 fd = getpfs(&pfs, av[0]); 512 513 printf("%s\n", av[0]); 514 fflush(stdout); 515 516 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 517 parse_pfsd_options(av + 1, ac - 1, pfs.ondisk); 518 if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) && 519 pfs.pfs_id == 0) { 520 printf("The real mount point cannot be made a PFS " 521 "slave, only PFS sub-directories can be made " 522 "slaves\n"); 523 exit(1); 524 } 525 pfs.bytes = sizeof(*pfs.ondisk); 526 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) { 527 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 528 dump_pfsd(pfs.ondisk, fd); 529 } else { 530 printf("Unable to retrieve pfs configuration " 531 "after successful update: %s\n", 532 strerror(errno)); 533 exit(1); 534 } 535 } else { 536 printf("Unable to adjust pfs configuration: %s\n", 537 strerror(errno)); 538 exit(1); 539 } 540 } 541 relpfs(fd, &pfs); 542 } 543 544 static void 545 init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave) 546 { 547 uint32_t status; 548 549 bzero(pfsd, sizeof(*pfsd)); 550 pfsd->sync_beg_tid = 1; 551 pfsd->sync_end_tid = 1; 552 pfsd->sync_beg_ts = 0; 553 pfsd->sync_end_ts = 0; 554 uuid_create(&pfsd->shared_uuid, &status); 555 uuid_create(&pfsd->unique_uuid, &status); 556 if (is_slave) 557 pfsd->mirror_flags |= HAMMER_PFSD_SLAVE; 558 } 559 560 void 561 dump_pfsd(hammer_pseudofs_data_t pfsd, int fd) 562 { 563 struct hammer_ioc_version version; 564 uint32_t status; 565 char *str = NULL; 566 567 printf(" sync-beg-tid=0x%016jx\n", (uintmax_t)pfsd->sync_beg_tid); 568 printf(" sync-end-tid=0x%016jx\n", (uintmax_t)pfsd->sync_end_tid); 569 uuid_to_string(&pfsd->shared_uuid, &str, &status); 570 printf(" shared-uuid=%s\n", str); 571 free(str); 572 uuid_to_string(&pfsd->unique_uuid, &str, &status); 573 printf(" unique-uuid=%s\n", str); 574 free(str); 575 printf(" label=\"%s\"\n", pfsd->label); 576 if (pfsd->snapshots[0]) 577 printf(" snapshots=\"%s\"\n", pfsd->snapshots); 578 if (pfsd->prune_min < (60 * 60 * 24)) { 579 printf(" prune-min=%02d:%02d:%02d\n", 580 pfsd->prune_min / 60 / 60 % 24, 581 pfsd->prune_min / 60 % 60, 582 pfsd->prune_min % 60); 583 } else if (pfsd->prune_min % (60 * 60 * 24)) { 584 printf(" prune-min=%dd/%02d:%02d:%02d\n", 585 pfsd->prune_min / 60 / 60 / 24, 586 pfsd->prune_min / 60 / 60 % 24, 587 pfsd->prune_min / 60 % 60, 588 pfsd->prune_min % 60); 589 } else { 590 printf(" prune-min=%dd\n", pfsd->prune_min / 60 / 60 / 24); 591 } 592 593 if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) { 594 printf(" operating as a SLAVE\n"); 595 } else { 596 printf(" operating as a MASTER\n"); 597 } 598 599 /* 600 * Snapshots directory cannot be shown when there is no fd since 601 * hammer version can't be retrieved. mirror-dump passes -1 because 602 * its input came from mirror-read output thus no path is available 603 * to open(2). 604 */ 605 if (fd >= 0 && pfsd->snapshots[0] == 0) { 606 bzero(&version, sizeof(version)); 607 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) 608 return; 609 if (version.cur_version < 3) { 610 if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) { 611 printf(" snapshots directory not set for " 612 "slave\n"); 613 } else { 614 printf(" snapshots directory for master " 615 "defaults to <pfs>/snapshots\n"); 616 } 617 } else { 618 printf(" snapshots directory defaults to " 619 "/var/hammer/<pfs>\n"); 620 } 621 } 622 } 623 624 static void 625 parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd) 626 { 627 char *cmd; 628 char *ptr; 629 int len; 630 uint32_t status; 631 632 while (ac) { 633 cmd = *av; 634 if ((ptr = strchr(cmd, '=')) != NULL) 635 *ptr++ = 0; 636 637 /* 638 * Basic assignment value test 639 */ 640 if (ptr == NULL) { 641 fprintf(stderr, 642 "option %s requires an assignment\n", 643 cmd); 644 exit(1); 645 } 646 647 status = uuid_s_ok; 648 if (strcmp(cmd, "sync-beg-tid") == 0) { 649 pfsd->sync_beg_tid = strtoull(ptr, NULL, 16); 650 } else if (strcmp(cmd, "sync-end-tid") == 0) { 651 pfsd->sync_end_tid = strtoull(ptr, NULL, 16); 652 } else if (strcmp(cmd, "shared-uuid") == 0) { 653 uuid_from_string(ptr, &pfsd->shared_uuid, &status); 654 } else if (strcmp(cmd, "unique-uuid") == 0) { 655 uuid_from_string(ptr, &pfsd->unique_uuid, &status); 656 } else if (strcmp(cmd, "label") == 0) { 657 len = strlen(ptr); 658 if (ptr[0] == '"' && ptr[len-1] == '"') { 659 ptr[len-1] = 0; 660 ++ptr; 661 } else if (ptr[0] == '"') { 662 fprintf(stderr, 663 "option %s: malformed string\n", 664 cmd); 665 exit(1); 666 } 667 snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr); 668 } else if (strcmp(cmd, "snapshots") == 0) { 669 len = strlen(ptr); 670 if (ptr[0] != '/') { 671 fprintf(stderr, 672 "option %s: '%s' must be an " 673 "absolute path\n", cmd, ptr); 674 if (ptr[0] == 0) { 675 fprintf(stderr, 676 "use 'snapshots-clear' " 677 "to unset snapshots dir\n"); 678 } 679 exit(1); 680 } 681 if (len >= (int)sizeof(pfsd->snapshots)) { 682 fprintf(stderr, 683 "option %s: path too long, %d " 684 "character limit\n", cmd, len); 685 exit(1); 686 } 687 snprintf(pfsd->snapshots, sizeof(pfsd->snapshots), 688 "%s", ptr); 689 } else if (strcmp(cmd, "snapshots-clear") == 0) { 690 pfsd->snapshots[0] = 0; 691 } else if (strcmp(cmd, "prune-min") == 0) { 692 pfsd->prune_min = timetosecs(ptr); 693 if (pfsd->prune_min < 0) { 694 fprintf(stderr, 695 "option %s: illegal time spec, " 696 "use Nd or [Nd/]hh[:mm[:ss]]\n", ptr); 697 exit(1); 698 } 699 } else { 700 fprintf(stderr, "invalid option: %s\n", cmd); 701 exit(1); 702 } 703 if (status != uuid_s_ok) { 704 fprintf(stderr, "option %s: error parsing uuid %s\n", 705 cmd, ptr); 706 exit(1); 707 } 708 --ac; 709 ++av; 710 } 711 } 712 713 static 714 void 715 pseudofs_usage(int code) 716 { 717 fprintf(stderr, 718 "hammer pfs-status <dirpath> ...\n" 719 "hammer pfs-master <dirpath> [options]\n" 720 "hammer pfs-slave <dirpath> [options]\n" 721 "hammer pfs-update <dirpath> [options]\n" 722 "hammer pfs-upgrade <dirpath>\n" 723 "hammer pfs-downgrade <dirpath>\n" 724 "hammer pfs-destroy <dirpath>\n" 725 "\n" 726 "options:\n" 727 " sync-beg-tid=0x16llx\n" 728 " sync-end-tid=0x16llx\n" 729 " shared-uuid=0x16llx\n" 730 " unique-uuid=0x16llx\n" 731 " label=\"string\"\n" 732 " snapshots=\"/path\"\n" 733 " snapshots-clear\n" 734 " prune-min=Nd\n" 735 " prune-min=[Nd/]hh[:mm[:ss]]\n" 736 ); 737 exit(code); 738 } 739 740 static 741 int 742 getyn(void) 743 { 744 char buf[256]; 745 int len; 746 747 if (fgets(buf, sizeof(buf), stdin) == NULL) 748 return(0); 749 len = strlen(buf); 750 while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) 751 --len; 752 buf[len] = 0; 753 if (strcmp(buf, "y") == 0 || 754 strcmp(buf, "yes") == 0 || 755 strcmp(buf, "Y") == 0 || 756 strcmp(buf, "YES") == 0) { 757 return(1); 758 } 759 return(0); 760 } 761 762 /* 763 * Convert time in the form [Nd/]hh[:mm[:ss]] to seconds. 764 * 765 * Return -1 if a parse error occurs. 766 * Return 0x7FFFFFFF if the time exceeds the maximum allowed. 767 */ 768 static 769 int 770 timetosecs(char *str) 771 { 772 int days = 0; 773 int hrs = 0; 774 int mins = 0; 775 int secs = 0; 776 int n; 777 long long v; 778 char *ptr; 779 780 n = strtol(str, &ptr, 10); 781 if (n < 0) 782 return(-1); 783 if (*ptr == 'd') { 784 days = n; 785 ++ptr; 786 if (*ptr == '/') 787 n = strtol(ptr + 1, &ptr, 10); 788 else 789 n = 0; 790 } 791 if (n < 0) 792 return(-1); 793 hrs = n; 794 if (*ptr == ':') { 795 n = strtol(ptr + 1, &ptr, 10); 796 if (n < 0) 797 return(-1); 798 mins = n; 799 if (*ptr == ':') { 800 n = strtol(ptr + 1, &ptr, 10); 801 if (n < 0) 802 return(-1); 803 secs = n; 804 } 805 } 806 if (*ptr) 807 return(-1); 808 v = days * 24 * 60 * 60 + hrs * 60 * 60 + mins * 60 + secs; 809 if (v > 0x7FFFFFFF) 810 v = 0x7FFFFFFF; 811 return((int)v); 812 } 813