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

*** 5360,5370 **** return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); } typedef struct holds_cbdata { boolean_t cb_recursive; ! const char *cb_snapname; nvlist_t **cb_nvlp; size_t cb_max_namelen; size_t cb_max_taglen; } holds_cbdata_t; --- 5360,5373 ---- return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); } typedef struct holds_cbdata { boolean_t cb_recursive; ! const char **cb_snapnames; ! char **cb_argv_filt; ! size_t cb_argc_filt; ! size_t cb_idx; nvlist_t **cb_nvlp; size_t cb_max_namelen; size_t cb_max_taglen; } holds_cbdata_t;
*** 5429,5449 **** nvlist_t *nvl = NULL; nvpair_t *nvp = NULL; const char *zname = zfs_get_name(zhp); size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN); ! if (cbp->cb_recursive) { ! const char *snapname; ! char *delim = strchr(zname, '@'); ! if (delim == NULL) return (0); ! snapname = delim + 1; ! if (strcmp(cbp->cb_snapname, snapname)) return (0); } if (zfs_get_holds(zhp, &nvl) != 0) return (-1); if (znamelen > cbp->cb_max_namelen) cbp->cb_max_namelen = znamelen; --- 5432,5482 ---- nvlist_t *nvl = NULL; nvpair_t *nvp = NULL; const char *zname = zfs_get_name(zhp); size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN); ! /* ! * If not a snapshot then we are running recursive and it ! * might be one of the arguments. In this case we have to bump ! * the index so we know which snapname to match the ! * descendants of this argument against. ! * ! * In any case we skip this zfs handle because we know it is ! * not a hold. ! */ ! if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { ! if ((cbp->cb_idx < cbp->cb_argc_filt) ! && (strcmp(cbp->cb_argv_filt[cbp->cb_idx], zname) == 0)) { ! cbp->cb_idx++; ! } return (0); + } ! if (cbp->cb_recursive == B_TRUE) { ! char *delim = strchr(zname, '@'); ! const char *snapname = delim + 1; ! ! /* ! * The snapshot name of this descendent doesn't match, ! * skip it. ! */ ! if (strcmp(cbp->cb_snapnames[cbp->cb_idx - 1], snapname) != 0) { return (0); } + } + /* + * In this case we know two things are true. + * + * 1. That zhp is a snapshot. + * + * 2. If a recursive listing then zhp is descendent of the + * user argument with a snapshot name that matches. + * + * In this case, gegrab the holds on the snapshot and add them + * to list. + */ if (zfs_get_holds(zhp, &nvl) != 0) return (-1); if (znamelen > cbp->cb_max_namelen) cbp->cb_max_namelen = znamelen;
*** 5466,5476 **** static int zfs_do_holds(int argc, char **argv) { int errors = 0; int c; ! int i; boolean_t scripted = B_FALSE; boolean_t recursive = B_FALSE; const char *opts = "rH"; nvlist_t *nvl; --- 5499,5511 ---- static int zfs_do_holds(int argc, char **argv) { int errors = 0; int c; ! char **argv_filt; ! int argc_filt = 0; ! const char **snapnames; boolean_t scripted = B_FALSE; boolean_t recursive = B_FALSE; const char *opts = "rH"; nvlist_t *nvl;
*** 5495,5557 **** optopt); usage(B_FALSE); } } - if (recursive) { - types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; - flags |= ZFS_ITER_RECURSE; - } - argc -= optind; argv += optind; /* check number of arguments */ if (argc < 1) usage(B_FALSE); if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) nomem(); ! for (i = 0; i < argc; ++i) { char *snapshot = argv[i]; ! const char *delim; ! const char *snapname; - delim = strchr(snapshot, '@'); if (delim == NULL) { (void) fprintf(stderr, gettext("'%s' is not a snapshot\n"), snapshot); ++errors; continue; } ! snapname = delim + 1; ! if (recursive) snapshot[delim - snapshot] = '\0'; ! cb.cb_recursive = recursive; ! cb.cb_snapname = snapname; ! cb.cb_nvlp = &nvl; /* * 1. collect holds data, set format options */ ! ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit, ! holds_callback, &cb); if (ret != 0) ++errors; - } /* * 2. print holds data */ print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl); if (nvlist_empty(nvl)) (void) printf(gettext("no datasets available\n")); nvlist_free(nvl); return (0 != errors); } #define CHECK_SPINNER 30 --- 5530,5658 ---- optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* check number of arguments */ if (argc < 1) usage(B_FALSE); if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) nomem(); ! if ((argv_filt = malloc(sizeof (char *) * argc)) == NULL) ! nomem(); ! ! if ((snapnames = malloc(sizeof (char *) * argc)) == NULL) ! nomem(); ! ! cb.cb_nvlp = &nvl; ! cb.cb_recursive = B_FALSE; ! cb.cb_idx = 0; ! ! if (recursive == B_TRUE) { ! types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; ! flags |= ZFS_ITER_RECURSE; ! cb.cb_recursive = B_TRUE; ! cb.cb_snapnames = malloc(sizeof (char *) * argc); ! ! if (cb.cb_snapnames == NULL) ! nomem(); ! } ! ! /* ! * Filter out non-snapshot arguments. Store pointers to valid ! * arguments in argv_filt. The total count of valid arguments ! * is stored in argc_filt. ! */ ! for (int i = 0; i < argc; i++) { char *snapshot = argv[i]; ! const char *delim = strchr(snapshot, '@'); if (delim == NULL) { (void) fprintf(stderr, gettext("'%s' is not a snapshot\n"), snapshot); ++errors; continue; } ! ! /* ! * If running a recusive (-r) listing then the ! * arguments to zfs_for_each() must each be stripped ! * of their snapshot suffix. Furthermore, when ! * descending down for a particular argument, only ! * snapshots with the same name as the original ! * argument should be returned. Since we just stripped ! * the snapname from the arguments they need to be ! * kept track of somewhere else. This is the purpose ! * of snapnames. As zfs_for_each() iterates the list ! * the callback uses cb_argv_filt and cb_snapnames to ! * make sure that only descendents of the same ! * snapname are listed. ! * ! * If this is confusing then it only means you are ! * human. The behavior of "zfs holds -r" is very odd. ! * ! * For example, given a filesystem layout like so: ! * ! * rpool/foo ! * rpool/foo@1 ! * rpool/foo/bar ! * rpool/foo/bar@1 ! * rpool/foo/bar@2 ! * rpool/baz ! * rpool/baz@1 ! * rpool/baz@2 ! * rpool/baz/blah ! * rpool/baz/blah@2 ! * ! * "zfs holds -r rpool/foo@1 rpool/baz@2" should list ! * holds for: ! * ! * rpool/foo@1 ! * rpool/foo/bar@1 ! * rpool/baz@2 ! * rpool/baz/blah@2 ! */ ! if (recursive == B_TRUE) { ! const char *snapname = delim + 1; snapshot[delim - snapshot] = '\0'; + snapnames[argc_filt] = snapname; + } ! argv_filt[argc_filt++] = snapshot; ! } ! ! if (argc_filt == 0) ! return (0 != errors); ! ! cb.cb_argv_filt = argv_filt; ! cb.cb_argc_filt = argc_filt; ! cb.cb_snapnames = snapnames; /* * 1. collect holds data, set format options */ ! ret = zfs_for_each(argc_filt, argv_filt, flags, types, NULL, NULL, ! limit, holds_callback, &cb); if (ret != 0) ++errors; /* * 2. print holds data */ print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl); if (nvlist_empty(nvl)) (void) printf(gettext("no datasets available\n")); nvlist_free(nvl); + free(snapnames); + free(argv_filt); return (0 != errors); } #define CHECK_SPINNER 30