Print this page
1869 "zfs holds" is O(n^2)


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) {