5345 zfs_do_hold(int argc, char **argv)
5346 {
5347 return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5348 }
5349
5350 /*
5351 * zfs release [-r] <tag> <snap> ...
5352 *
5353 * -r Recursively release
5354 *
5355 * Release a user-hold with the given tag from the list of snapshots.
5356 */
5357 static int
5358 zfs_do_release(int argc, char **argv)
5359 {
5360 return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5361 }
5362
5363 typedef struct holds_cbdata {
5364 boolean_t cb_recursive;
5365 const char *cb_snapname;
5366 nvlist_t **cb_nvlp;
5367 size_t cb_max_namelen;
5368 size_t cb_max_taglen;
5369 } holds_cbdata_t;
5370
5371 #define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
5372 #define DATETIME_BUF_LEN (32)
5373 /*
5374 *
5375 */
5376 static void
5377 print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
5378 {
5379 int i;
5380 nvpair_t *nvp = NULL;
5381 char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
5382 const char *col;
5383
5384 if (!scripted) {
5385 for (i = 0; i < 3; i++) {
5414
5415 (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname,
5416 sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf);
5417 }
5418 }
5419 }
5420
5421 /*
5422 * Generic callback function to list a dataset or snapshot.
5423 */
5424 static int
5425 holds_callback(zfs_handle_t *zhp, void *data)
5426 {
5427 holds_cbdata_t *cbp = data;
5428 nvlist_t *top_nvl = *cbp->cb_nvlp;
5429 nvlist_t *nvl = NULL;
5430 nvpair_t *nvp = NULL;
5431 const char *zname = zfs_get_name(zhp);
5432 size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN);
5433
5434 if (cbp->cb_recursive) {
5435 const char *snapname;
5436 char *delim = strchr(zname, '@');
5437 if (delim == NULL)
5438 return (0);
5439
5440 snapname = delim + 1;
5441 if (strcmp(cbp->cb_snapname, snapname))
5442 return (0);
5443 }
5444
5445 if (zfs_get_holds(zhp, &nvl) != 0)
5446 return (-1);
5447
5448 if (znamelen > cbp->cb_max_namelen)
5449 cbp->cb_max_namelen = znamelen;
5450
5451 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5452 const char *tag = nvpair_name(nvp);
5453 size_t taglen = strnlen(tag, MAXNAMELEN);
5454 if (taglen > cbp->cb_max_taglen)
5455 cbp->cb_max_taglen = taglen;
5456 }
5457
5458 return (nvlist_add_nvlist(top_nvl, zname, nvl));
5459 }
5460
5461 /*
5462 * zfs holds [-r] <snap> ...
5463 *
5464 * -r Recursively hold
5465 */
5466 static int
5467 zfs_do_holds(int argc, char **argv)
5468 {
5469 int errors = 0;
5470 int c;
5471 int i;
5472 boolean_t scripted = B_FALSE;
5473 boolean_t recursive = B_FALSE;
5474 const char *opts = "rH";
5475 nvlist_t *nvl;
5476
5477 int types = ZFS_TYPE_SNAPSHOT;
5478 holds_cbdata_t cb = { 0 };
5479
5480 int limit = 0;
5481 int ret = 0;
5482 int flags = 0;
5483
5484 /* check options */
5485 while ((c = getopt(argc, argv, opts)) != -1) {
5486 switch (c) {
5487 case 'r':
5488 recursive = B_TRUE;
5489 break;
5490 case 'H':
5491 scripted = B_TRUE;
5492 break;
5493 case '?':
5494 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5495 optopt);
5496 usage(B_FALSE);
5497 }
5498 }
5499
5500 if (recursive) {
5501 types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
5502 flags |= ZFS_ITER_RECURSE;
5503 }
5504
5505 argc -= optind;
5506 argv += optind;
5507
5508 /* check number of arguments */
5509 if (argc < 1)
5510 usage(B_FALSE);
5511
5512 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
5513 nomem();
5514
5515 for (i = 0; i < argc; ++i) {
5516 char *snapshot = argv[i];
5517 const char *delim;
5518 const char *snapname;
5519
5520 delim = strchr(snapshot, '@');
5521 if (delim == NULL) {
5522 (void) fprintf(stderr,
5523 gettext("'%s' is not a snapshot\n"), snapshot);
5524 ++errors;
5525 continue;
5526 }
5527 snapname = delim + 1;
5528 if (recursive)
5529 snapshot[delim - snapshot] = '\0';
5530
5531 cb.cb_recursive = recursive;
5532 cb.cb_snapname = snapname;
5533 cb.cb_nvlp = &nvl;
5534
5535 /*
5536 * 1. collect holds data, set format options
5537 */
5538 ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
5539 holds_callback, &cb);
5540 if (ret != 0)
5541 ++errors;
5542 }
5543
5544 /*
5545 * 2. print holds data
5546 */
5547 print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5548
5549 if (nvlist_empty(nvl))
5550 (void) printf(gettext("no datasets available\n"));
5551
5552 nvlist_free(nvl);
5553
5554 return (0 != errors);
5555 }
5556
5557 #define CHECK_SPINNER 30
5558 #define SPINNER_TIME 3 /* seconds */
5559 #define MOUNT_TIME 5 /* seconds */
5560
5561 static int
5562 get_one_dataset(zfs_handle_t *zhp, void *data)
5563 {
5564 static char *spin[] = { "-", "\\", "|", "/" };
5565 static int spinval = 0;
5566 static int spincheck = 0;
5567 static time_t last_spin_time = (time_t)0;
5568 get_all_cb_t *cbp = data;
5569 zfs_type_t type = zfs_get_type(zhp);
5570
5571 if (cbp->cb_verbose) {
5572 if (--spincheck < 0) {
|
5345 zfs_do_hold(int argc, char **argv)
5346 {
5347 return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5348 }
5349
5350 /*
5351 * zfs release [-r] <tag> <snap> ...
5352 *
5353 * -r Recursively release
5354 *
5355 * Release a user-hold with the given tag from the list of snapshots.
5356 */
5357 static int
5358 zfs_do_release(int argc, char **argv)
5359 {
5360 return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5361 }
5362
5363 typedef struct holds_cbdata {
5364 boolean_t cb_recursive;
5365 const char **cb_snapnames;
5366 char **cb_argv_filt;
5367 size_t cb_argc_filt;
5368 size_t cb_idx;
5369 nvlist_t **cb_nvlp;
5370 size_t cb_max_namelen;
5371 size_t cb_max_taglen;
5372 } holds_cbdata_t;
5373
5374 #define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
5375 #define DATETIME_BUF_LEN (32)
5376 /*
5377 *
5378 */
5379 static void
5380 print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
5381 {
5382 int i;
5383 nvpair_t *nvp = NULL;
5384 char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
5385 const char *col;
5386
5387 if (!scripted) {
5388 for (i = 0; i < 3; i++) {
5417
5418 (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname,
5419 sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf);
5420 }
5421 }
5422 }
5423
5424 /*
5425 * Generic callback function to list a dataset or snapshot.
5426 */
5427 static int
5428 holds_callback(zfs_handle_t *zhp, void *data)
5429 {
5430 holds_cbdata_t *cbp = data;
5431 nvlist_t *top_nvl = *cbp->cb_nvlp;
5432 nvlist_t *nvl = NULL;
5433 nvpair_t *nvp = NULL;
5434 const char *zname = zfs_get_name(zhp);
5435 size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN);
5436
5437 /*
5438 * If not a snapshot then we are running recursive and it
5439 * might be one of the arguments. In this case we have to bump
5440 * the index so we know which snapname to match the
5441 * descendants of this argument against.
5442 *
5443 * In any case we skip this zfs handle because we know it is
5444 * not a hold.
5445 */
5446 if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
5447 if ((cbp->cb_idx < cbp->cb_argc_filt)
5448 && (strcmp(cbp->cb_argv_filt[cbp->cb_idx], zname) == 0)) {
5449 cbp->cb_idx++;
5450 }
5451 return (0);
5452 }
5453
5454 if (cbp->cb_recursive == B_TRUE) {
5455 char *delim = strchr(zname, '@');
5456 const char *snapname = delim + 1;
5457
5458 /*
5459 * The snapshot name of this descendent doesn't match,
5460 * skip it.
5461 */
5462 if (strcmp(cbp->cb_snapnames[cbp->cb_idx - 1], snapname) != 0) {
5463 return (0);
5464 }
5465 }
5466
5467 /*
5468 * In this case we know two things are true.
5469 *
5470 * 1. That zhp is a snapshot.
5471 *
5472 * 2. If a recursive listing then zhp is descendent of the
5473 * user argument with a snapshot name that matches.
5474 *
5475 * In this case, gegrab the holds on the snapshot and add them
5476 * to list.
5477 */
5478 if (zfs_get_holds(zhp, &nvl) != 0)
5479 return (-1);
5480
5481 if (znamelen > cbp->cb_max_namelen)
5482 cbp->cb_max_namelen = znamelen;
5483
5484 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5485 const char *tag = nvpair_name(nvp);
5486 size_t taglen = strnlen(tag, MAXNAMELEN);
5487 if (taglen > cbp->cb_max_taglen)
5488 cbp->cb_max_taglen = taglen;
5489 }
5490
5491 return (nvlist_add_nvlist(top_nvl, zname, nvl));
5492 }
5493
5494 /*
5495 * zfs holds [-r] <snap> ...
5496 *
5497 * -r Recursively hold
5498 */
5499 static int
5500 zfs_do_holds(int argc, char **argv)
5501 {
5502 int errors = 0;
5503 int c;
5504 char **argv_filt;
5505 int argc_filt = 0;
5506 const char **snapnames;
5507 boolean_t scripted = B_FALSE;
5508 boolean_t recursive = B_FALSE;
5509 const char *opts = "rH";
5510 nvlist_t *nvl;
5511
5512 int types = ZFS_TYPE_SNAPSHOT;
5513 holds_cbdata_t cb = { 0 };
5514
5515 int limit = 0;
5516 int ret = 0;
5517 int flags = 0;
5518
5519 /* check options */
5520 while ((c = getopt(argc, argv, opts)) != -1) {
5521 switch (c) {
5522 case 'r':
5523 recursive = B_TRUE;
5524 break;
5525 case 'H':
5526 scripted = B_TRUE;
5527 break;
5528 case '?':
5529 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5530 optopt);
5531 usage(B_FALSE);
5532 }
5533 }
5534
5535 argc -= optind;
5536 argv += optind;
5537
5538 /* check number of arguments */
5539 if (argc < 1)
5540 usage(B_FALSE);
5541
5542 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
5543 nomem();
5544
5545 if ((argv_filt = malloc(sizeof (char *) * argc)) == NULL)
5546 nomem();
5547
5548 if ((snapnames = malloc(sizeof (char *) * argc)) == NULL)
5549 nomem();
5550
5551 cb.cb_nvlp = &nvl;
5552 cb.cb_recursive = B_FALSE;
5553 cb.cb_idx = 0;
5554
5555 if (recursive == B_TRUE) {
5556 types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
5557 flags |= ZFS_ITER_RECURSE;
5558 cb.cb_recursive = B_TRUE;
5559 cb.cb_snapnames = malloc(sizeof (char *) * argc);
5560
5561 if (cb.cb_snapnames == NULL)
5562 nomem();
5563 }
5564
5565 /*
5566 * Filter out non-snapshot arguments. Store pointers to valid
5567 * arguments in argv_filt. The total count of valid arguments
5568 * is stored in argc_filt.
5569 */
5570 for (int i = 0; i < argc; i++) {
5571 char *snapshot = argv[i];
5572 const char *delim = strchr(snapshot, '@');
5573
5574 if (delim == NULL) {
5575 (void) fprintf(stderr,
5576 gettext("'%s' is not a snapshot\n"), snapshot);
5577 ++errors;
5578 continue;
5579 }
5580
5581 /*
5582 * If running a recusive (-r) listing then the
5583 * arguments to zfs_for_each() must each be stripped
5584 * of their snapshot suffix. Furthermore, when
5585 * descending down for a particular argument, only
5586 * snapshots with the same name as the original
5587 * argument should be returned. Since we just stripped
5588 * the snapname from the arguments they need to be
5589 * kept track of somewhere else. This is the purpose
5590 * of snapnames. As zfs_for_each() iterates the list
5591 * the callback uses cb_argv_filt and cb_snapnames to
5592 * make sure that only descendents of the same
5593 * snapname are listed.
5594 *
5595 * If this is confusing then it only means you are
5596 * human. The behavior of "zfs holds -r" is very odd.
5597 *
5598 * For example, given a filesystem layout like so:
5599 *
5600 * rpool/foo
5601 * rpool/foo@1
5602 * rpool/foo/bar
5603 * rpool/foo/bar@1
5604 * rpool/foo/bar@2
5605 * rpool/baz
5606 * rpool/baz@1
5607 * rpool/baz@2
5608 * rpool/baz/blah
5609 * rpool/baz/blah@2
5610 *
5611 * "zfs holds -r rpool/foo@1 rpool/baz@2" should list
5612 * holds for:
5613 *
5614 * rpool/foo@1
5615 * rpool/foo/bar@1
5616 * rpool/baz@2
5617 * rpool/baz/blah@2
5618 */
5619 if (recursive == B_TRUE) {
5620 const char *snapname = delim + 1;
5621 snapshot[delim - snapshot] = '\0';
5622 snapnames[argc_filt] = snapname;
5623 }
5624
5625 argv_filt[argc_filt++] = snapshot;
5626 }
5627
5628 if (argc_filt == 0)
5629 return (0 != errors);
5630
5631 cb.cb_argv_filt = argv_filt;
5632 cb.cb_argc_filt = argc_filt;
5633 cb.cb_snapnames = snapnames;
5634
5635 /*
5636 * 1. collect holds data, set format options
5637 */
5638 ret = zfs_for_each(argc_filt, argv_filt, flags, types, NULL, NULL,
5639 limit, holds_callback, &cb);
5640 if (ret != 0)
5641 ++errors;
5642
5643 /*
5644 * 2. print holds data
5645 */
5646 print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5647
5648 if (nvlist_empty(nvl))
5649 (void) printf(gettext("no datasets available\n"));
5650
5651 nvlist_free(nvl);
5652 free(snapnames);
5653 free(argv_filt);
5654
5655 return (0 != errors);
5656 }
5657
5658 #define CHECK_SPINNER 30
5659 #define SPINNER_TIME 3 /* seconds */
5660 #define MOUNT_TIME 5 /* seconds */
5661
5662 static int
5663 get_one_dataset(zfs_handle_t *zhp, void *data)
5664 {
5665 static char *spin[] = { "-", "\\", "|", "/" };
5666 static int spinval = 0;
5667 static int spincheck = 0;
5668 static time_t last_spin_time = (time_t)0;
5669 get_all_cb_t *cbp = data;
5670 zfs_type_t type = zfs_get_type(zhp);
5671
5672 if (cbp->cb_verbose) {
5673 if (--spincheck < 0) {
|