Print this page
2882 implement libzfs_core
2883 changing "canmount" property to "on" should not always remount dataset
2900 "zfs snapshot" should be able to create multiple, arbitrary snapshots at once
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Chris Siden <christopher.siden@delphix.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Bill Pijewski <wdp@joyent.com>
Reviewed by: Dan Kruchinin <dan.kruchinin@gmail.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libzfs/common/libzfs_dataset.c
          +++ new/usr/src/lib/libzfs/common/libzfs_dataset.c
↓ open down ↓ 1399 lines elided ↑ open up ↑
1400 1400  int
1401 1401  zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1402 1402  {
1403 1403          zfs_cmd_t zc = { 0 };
1404 1404          int ret = -1;
1405 1405          prop_changelist_t *cl = NULL;
1406 1406          char errbuf[1024];
1407 1407          libzfs_handle_t *hdl = zhp->zfs_hdl;
1408 1408          nvlist_t *nvl = NULL, *realprops;
1409 1409          zfs_prop_t prop;
1410      -        boolean_t do_prefix;
1411      -        uint64_t idx;
     1410 +        boolean_t do_prefix = B_TRUE;
1412 1411          int added_resv;
1413 1412  
1414 1413          (void) snprintf(errbuf, sizeof (errbuf),
1415 1414              dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
1416 1415              zhp->zfs_name);
1417 1416  
1418 1417          if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1419 1418              nvlist_add_string(nvl, propname, propval) != 0) {
1420 1419                  (void) no_memory(hdl);
1421 1420                  goto error;
↓ open down ↓ 18 lines elided ↑ open up ↑
1440 1439  
1441 1440          if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1442 1441                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1443 1442                      "child dataset with inherited mountpoint is used "
1444 1443                      "in a non-global zone"));
1445 1444                  ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1446 1445                  goto error;
1447 1446          }
1448 1447  
1449 1448          /*
1450      -         * If the dataset's canmount property is being set to noauto,
1451      -         * then we want to prevent unmounting & remounting it.
1452      -         */
1453      -        do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
1454      -            (zprop_string_to_index(prop, propval, &idx,
1455      -            ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
     1449 +         * We don't want to unmount & remount the dataset when changing
     1450 +         * its canmount property to 'on' or 'noauto'.  We only use
     1451 +         * the changelist logic to unmount when setting canmount=off.
     1452 +         */
     1453 +        if (prop == ZFS_PROP_CANMOUNT) {
     1454 +                uint64_t idx;
     1455 +                int err = zprop_string_to_index(prop, propval, &idx,
     1456 +                    ZFS_TYPE_DATASET);
     1457 +                if (err == 0 && idx != ZFS_CANMOUNT_OFF)
     1458 +                        do_prefix = B_FALSE;
     1459 +        }
1456 1460  
1457 1461          if (do_prefix && (ret = changelist_prefix(cl)) != 0)
1458 1462                  goto error;
1459 1463  
1460 1464          /*
1461 1465           * Execute the corresponding ioctl() to set this property.
1462 1466           */
1463 1467          (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1464 1468  
1465 1469          if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
↓ open down ↓ 1168 lines elided ↑ open up ↑
2634 2638                  return (err);
2635 2639  
2636 2640          if (literal) {
2637 2641                  (void) snprintf(propbuf, proplen, "%llu", propvalue);
2638 2642          } else {
2639 2643                  zfs_nicenum(propvalue, propbuf, proplen);
2640 2644          }
2641 2645          return (0);
2642 2646  }
2643 2647  
2644      -int
2645      -zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
2646      -    uint64_t *usedp)
2647      -{
2648      -        int err;
2649      -        zfs_cmd_t zc = { 0 };
2650      -
2651      -        (void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
2652      -        (void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
2653      -
2654      -        err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
2655      -        if (err)
2656      -                return (err);
2657      -
2658      -        *usedp = zc.zc_cookie;
2659      -
2660      -        return (0);
2661      -}
2662      -
2663 2648  /*
2664 2649   * Returns the name of the given zfs handle.
2665 2650   */
2666 2651  const char *
2667 2652  zfs_get_name(const zfs_handle_t *zhp)
2668 2653  {
2669 2654          return (zhp->zfs_name);
2670 2655  }
2671 2656  
2672 2657  /*
↓ open down ↓ 180 lines elided ↑ open up ↑
2853 2838          if (h == NULL)
2854 2839                  return (-1);
2855 2840          zfs_close(h);
2856 2841  
2857 2842          /*
2858 2843           * Attempt to create, mount, and share any ancestor filesystems,
2859 2844           * up to the prefixlen-long one.
2860 2845           */
2861 2846          for (cp = target + prefixlen + 1;
2862 2847              cp = strchr(cp, '/'); *cp = '/', cp++) {
2863      -                char *logstr;
2864 2848  
2865 2849                  *cp = '\0';
2866 2850  
2867 2851                  h = make_dataset_handle(hdl, target);
2868 2852                  if (h) {
2869 2853                          /* it already exists, nothing to do here */
2870 2854                          zfs_close(h);
2871 2855                          continue;
2872 2856                  }
2873 2857  
2874      -                logstr = hdl->libzfs_log_str;
2875      -                hdl->libzfs_log_str = NULL;
2876 2858                  if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
2877 2859                      NULL) != 0) {
2878      -                        hdl->libzfs_log_str = logstr;
2879 2860                          opname = dgettext(TEXT_DOMAIN, "create");
2880 2861                          goto ancestorerr;
2881 2862                  }
2882 2863  
2883      -                hdl->libzfs_log_str = logstr;
2884 2864                  h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2885 2865                  if (h == NULL) {
2886 2866                          opname = dgettext(TEXT_DOMAIN, "open");
2887 2867                          goto ancestorerr;
2888 2868                  }
2889 2869  
2890 2870                  if (zfs_mount(h, NULL, 0) != 0) {
2891 2871                          opname = dgettext(TEXT_DOMAIN, "mount");
2892 2872                          goto ancestorerr;
2893 2873                  }
↓ open down ↓ 37 lines elided ↑ open up ↑
2931 2911          return (0);
2932 2912  }
2933 2913  
2934 2914  /*
2935 2915   * Create a new filesystem or volume.
2936 2916   */
2937 2917  int
2938 2918  zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
2939 2919      nvlist_t *props)
2940 2920  {
2941      -        zfs_cmd_t zc = { 0 };
2942 2921          int ret;
2943 2922          uint64_t size = 0;
2944 2923          uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
2945 2924          char errbuf[1024];
2946 2925          uint64_t zoned;
     2926 +        dmu_objset_type_t ost;
2947 2927  
2948 2928          (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2949 2929              "cannot create '%s'"), path);
2950 2930  
2951 2931          /* validate the path, taking care to note the extended error message */
2952 2932          if (!zfs_validate_name(hdl, path, type, B_TRUE))
2953 2933                  return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2954 2934  
2955 2935          /* validate parents exist */
2956 2936          if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
2957 2937                  return (-1);
2958 2938  
2959 2939          /*
2960 2940           * The failure modes when creating a dataset of a different type over
2961 2941           * one that already exists is a little strange.  In particular, if you
2962 2942           * try to create a dataset on top of an existing dataset, the ioctl()
2963 2943           * will return ENOENT, not EEXIST.  To prevent this from happening, we
2964 2944           * first try to see if the dataset exists.
2965 2945           */
2966      -        (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
2967      -        if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
     2946 +        if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
2968 2947                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2969 2948                      "dataset already exists"));
2970 2949                  return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2971 2950          }
2972 2951  
2973 2952          if (type == ZFS_TYPE_VOLUME)
2974      -                zc.zc_objset_type = DMU_OST_ZVOL;
     2953 +                ost = DMU_OST_ZVOL;
2975 2954          else
2976      -                zc.zc_objset_type = DMU_OST_ZFS;
     2955 +                ost = DMU_OST_ZFS;
2977 2956  
2978 2957          if (props && (props = zfs_valid_proplist(hdl, type, props,
2979 2958              zoned, NULL, errbuf)) == 0)
2980 2959                  return (-1);
2981 2960  
2982 2961          if (type == ZFS_TYPE_VOLUME) {
2983 2962                  /*
2984 2963                   * If we are creating a volume, the size and block size must
2985 2964                   * satisfy a few restraints.  First, the blocksize must be a
2986 2965                   * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
↓ open down ↓ 31 lines elided ↑ open up ↑
3018 2997  
3019 2998                  if (size % blocksize != 0) {
3020 2999                          nvlist_free(props);
3021 3000                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3022 3001                              "volume size must be a multiple of volume block "
3023 3002                              "size"));
3024 3003                          return (zfs_error(hdl, EZFS_BADPROP, errbuf));
3025 3004                  }
3026 3005          }
3027 3006  
3028      -        if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
3029      -                return (-1);
3030      -        nvlist_free(props);
3031      -
3032 3007          /* create the dataset */
3033      -        ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
3034      -
3035      -        zcmd_free_nvlists(&zc);
     3008 +        ret = lzc_create(path, ost, props);
     3009 +        nvlist_free(props);
3036 3010  
3037 3011          /* check for failure */
3038 3012          if (ret != 0) {
3039 3013                  char parent[ZFS_MAXNAMELEN];
3040 3014                  (void) parent_name(path, parent, sizeof (parent));
3041 3015  
3042 3016                  switch (errno) {
3043 3017                  case ENOENT:
3044 3018                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3045 3019                              "no such parent '%s'"), parent);
↓ open down ↓ 117 lines elided ↑ open up ↑
3163 3137  }
3164 3138  
3165 3139  /*
3166 3140   * Destroys all the snapshots named in the nvlist.  They must be underneath
3167 3141   * the zhp (either snapshots of it, or snapshots of its descendants).
3168 3142   */
3169 3143  int
3170 3144  zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
3171 3145  {
3172 3146          int ret;
3173      -        zfs_cmd_t zc = { 0 };
     3147 +        nvlist_t *errlist;
3174 3148  
3175      -        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3176      -        if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
3177      -                return (-1);
3178      -        zc.zc_defer_destroy = defer;
     3149 +        ret = lzc_destroy_snaps(snaps, defer, &errlist);
3179 3150  
3180      -        ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
3181 3151          if (ret != 0) {
3182      -                char errbuf[1024];
3183      -
3184      -                (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3185      -                    "cannot destroy snapshots in %s"), zc.zc_name);
3186      -
3187      -                switch (errno) {
3188      -                case EEXIST:
3189      -                        zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3190      -                            "snapshot is cloned"));
3191      -                        return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
     3152 +                for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
     3153 +                    pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
     3154 +                        char errbuf[1024];
     3155 +                        (void) snprintf(errbuf, sizeof (errbuf),
     3156 +                            dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
     3157 +                            nvpair_name(pair));
3192 3158  
3193      -                default:
3194      -                        return (zfs_standard_error(zhp->zfs_hdl, errno,
3195      -                            errbuf));
     3159 +                        switch (fnvpair_value_int32(pair)) {
     3160 +                        case EEXIST:
     3161 +                                zfs_error_aux(zhp->zfs_hdl,
     3162 +                                    dgettext(TEXT_DOMAIN,
     3163 +                                    "snapshot is cloned"));
     3164 +                                ret = zfs_error(zhp->zfs_hdl, EZFS_EXISTS,
     3165 +                                    errbuf);
     3166 +                                break;
     3167 +                        default:
     3168 +                                ret = zfs_standard_error(zhp->zfs_hdl, errno,
     3169 +                                    errbuf);
     3170 +                                break;
     3171 +                        }
3196 3172                  }
3197 3173          }
3198 3174  
3199      -        return (0);
     3175 +        return (ret);
3200 3176  }
3201 3177  
3202 3178  /*
3203 3179   * Clones the given dataset.  The target must be of the same type as the source.
3204 3180   */
3205 3181  int
3206 3182  zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
3207 3183  {
3208      -        zfs_cmd_t zc = { 0 };
3209 3184          char parent[ZFS_MAXNAMELEN];
3210 3185          int ret;
3211 3186          char errbuf[1024];
3212 3187          libzfs_handle_t *hdl = zhp->zfs_hdl;
3213      -        zfs_type_t type;
3214 3188          uint64_t zoned;
3215 3189  
3216 3190          assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
3217 3191  
3218 3192          (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3219 3193              "cannot create '%s'"), target);
3220 3194  
3221 3195          /* validate the target/clone name */
3222 3196          if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
3223 3197                  return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3224 3198  
3225 3199          /* validate parents exist */
3226 3200          if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
3227 3201                  return (-1);
3228 3202  
3229 3203          (void) parent_name(target, parent, sizeof (parent));
3230 3204  
3231 3205          /* do the clone */
3232      -        if (ZFS_IS_VOLUME(zhp)) {
3233      -                zc.zc_objset_type = DMU_OST_ZVOL;
3234      -                type = ZFS_TYPE_VOLUME;
3235      -        } else {
3236      -                zc.zc_objset_type = DMU_OST_ZFS;
3237      -                type = ZFS_TYPE_FILESYSTEM;
3238      -        }
3239 3206  
3240 3207          if (props) {
     3208 +                zfs_type_t type;
     3209 +                if (ZFS_IS_VOLUME(zhp)) {
     3210 +                        type = ZFS_TYPE_VOLUME;
     3211 +                } else {
     3212 +                        type = ZFS_TYPE_FILESYSTEM;
     3213 +                }
3241 3214                  if ((props = zfs_valid_proplist(hdl, type, props, zoned,
3242 3215                      zhp, errbuf)) == NULL)
3243 3216                          return (-1);
3244      -
3245      -                if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3246      -                        nvlist_free(props);
3247      -                        return (-1);
3248      -                }
3249      -
3250      -                nvlist_free(props);
3251 3217          }
3252 3218  
3253      -        (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
3254      -        (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
3255      -        ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
3256      -
3257      -        zcmd_free_nvlists(&zc);
     3219 +        ret = lzc_clone(target, zhp->zfs_name, props);
     3220 +        nvlist_free(props);
3258 3221  
3259 3222          if (ret != 0) {
3260 3223                  switch (errno) {
3261 3224  
3262 3225                  case ENOENT:
3263 3226                          /*
3264 3227                           * The parent doesn't exist.  We should have caught this
3265 3228                           * above, but there may a race condition that has since
3266 3229                           * destroyed the parent.
3267 3230                           *
↓ open down ↓ 64 lines elided ↑ open up ↑
3332 3295                              zc.zc_string, parent);
3333 3296                          return (zfs_error(hdl, EZFS_EXISTS, errbuf));
3334 3297  
3335 3298                  default:
3336 3299                          return (zfs_standard_error(hdl, save_errno, errbuf));
3337 3300                  }
3338 3301          }
3339 3302          return (ret);
3340 3303  }
3341 3304  
     3305 +typedef struct snapdata {
     3306 +        nvlist_t *sd_nvl;
     3307 +        const char *sd_snapname;
     3308 +} snapdata_t;
     3309 +
     3310 +static int
     3311 +zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
     3312 +{
     3313 +        snapdata_t *sd = arg;
     3314 +        char name[ZFS_MAXNAMELEN];
     3315 +        int rv = 0;
     3316 +
     3317 +        (void) snprintf(name, sizeof (name),
     3318 +            "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
     3319 +
     3320 +        fnvlist_add_boolean(sd->sd_nvl, name);
     3321 +
     3322 +        rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
     3323 +        zfs_close(zhp);
     3324 +        return (rv);
     3325 +}
     3326 +
3342 3327  /*
3343      - * Takes a snapshot of the given dataset.
     3328 + * Creates snapshots.  The keys in the snaps nvlist are the snapshots to be
     3329 + * created.
3344 3330   */
3345 3331  int
3346      -zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
3347      -    nvlist_t *props)
     3332 +zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
3348 3333  {
3349      -        const char *delim;
3350      -        char parent[ZFS_MAXNAMELEN];
3351      -        zfs_handle_t *zhp;
3352      -        zfs_cmd_t zc = { 0 };
3353 3334          int ret;
3354 3335          char errbuf[1024];
     3336 +        nvpair_t *elem;
     3337 +        nvlist_t *errors;
3355 3338  
3356 3339          (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3357      -            "cannot snapshot '%s'"), path);
     3340 +            "cannot create snapshots "));
3358 3341  
3359      -        /* validate the target name */
3360      -        if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
3361      -                return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
     3342 +        elem = NULL;
     3343 +        while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
     3344 +                const char *snapname = nvpair_name(elem);
     3345 +
     3346 +                /* validate the target name */
     3347 +                if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
     3348 +                    B_TRUE)) {
     3349 +                        (void) snprintf(errbuf, sizeof (errbuf),
     3350 +                            dgettext(TEXT_DOMAIN,
     3351 +                            "cannot create snapshot '%s'"), snapname);
     3352 +                        return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
     3353 +                }
     3354 +        }
3362 3355  
3363      -        if (props) {
3364      -                if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
3365      -                    props, B_FALSE, NULL, errbuf)) == NULL)
3366      -                        return (-1);
     3356 +        if (props != NULL &&
     3357 +            (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
     3358 +            props, B_FALSE, NULL, errbuf)) == NULL) {
     3359 +                return (-1);
     3360 +        }
3367 3361  
3368      -                if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3369      -                        nvlist_free(props);
3370      -                        return (-1);
3371      -                }
     3362 +        ret = lzc_snapshot(snaps, props, &errors);
     3363 +
     3364 +        if (ret != 0) {
     3365 +                boolean_t printed = B_FALSE;
     3366 +                for (elem = nvlist_next_nvpair(errors, NULL);
     3367 +                    elem != NULL;
     3368 +                    elem = nvlist_next_nvpair(errors, elem)) {
     3369 +                        (void) snprintf(errbuf, sizeof (errbuf),
     3370 +                            dgettext(TEXT_DOMAIN,
     3371 +                            "cannot create snapshot '%s'"), nvpair_name(elem));
     3372 +                        (void) zfs_standard_error(hdl,
     3373 +                            fnvpair_value_int32(elem), errbuf);
     3374 +                        printed = B_TRUE;
     3375 +                }
     3376 +                if (!printed) {
     3377 +                        switch (ret) {
     3378 +                        case EXDEV:
     3379 +                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
     3380 +                                    "multiple snapshots of same "
     3381 +                                    "fs not allowed"));
     3382 +                                (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3372 3383  
3373      -                nvlist_free(props);
     3384 +                                break;
     3385 +                        default:
     3386 +                                (void) zfs_standard_error(hdl, ret, errbuf);
     3387 +                        }
     3388 +                }
3374 3389          }
3375 3390  
3376      -        /* make sure the parent exists and is of the appropriate type */
3377      -        delim = strchr(path, '@');
3378      -        (void) strncpy(parent, path, delim - path);
3379      -        parent[delim - path] = '\0';
     3391 +        nvlist_free(props);
     3392 +        nvlist_free(errors);
     3393 +        return (ret);
     3394 +}
     3395 +
     3396 +int
     3397 +zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
     3398 +    nvlist_t *props)
     3399 +{
     3400 +        int ret;
     3401 +        snapdata_t sd = { 0 };
     3402 +        char fsname[ZFS_MAXNAMELEN];
     3403 +        char *cp;
     3404 +        zfs_handle_t *zhp;
     3405 +        char errbuf[1024];
     3406 +
     3407 +        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
     3408 +            "cannot snapshot %s"), path);
     3409 +
     3410 +        if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
     3411 +                return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3380 3412  
3381      -        if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
     3413 +        (void) strlcpy(fsname, path, sizeof (fsname));
     3414 +        cp = strchr(fsname, '@');
     3415 +        *cp = '\0';
     3416 +        sd.sd_snapname = cp + 1;
     3417 +
     3418 +        if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
3382 3419              ZFS_TYPE_VOLUME)) == NULL) {
3383      -                zcmd_free_nvlists(&zc);
3384 3420                  return (-1);
3385 3421          }
3386 3422  
3387      -        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3388      -        (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
3389      -        if (ZFS_IS_VOLUME(zhp))
3390      -                zc.zc_objset_type = DMU_OST_ZVOL;
3391      -        else
3392      -                zc.zc_objset_type = DMU_OST_ZFS;
3393      -        zc.zc_cookie = recursive;
3394      -        ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
3395      -
3396      -        zcmd_free_nvlists(&zc);
3397      -
3398      -        /*
3399      -         * if it was recursive, the one that actually failed will be in
3400      -         * zc.zc_name.
3401      -         */
3402      -        if (ret != 0) {
3403      -                (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3404      -                    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
3405      -                (void) zfs_standard_error(hdl, errno, errbuf);
     3423 +        verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
     3424 +        if (recursive) {
     3425 +                (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
     3426 +        } else {
     3427 +                fnvlist_add_boolean(sd.sd_nvl, path);
3406 3428          }
3407 3429  
     3430 +        ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
     3431 +        nvlist_free(sd.sd_nvl);
3408 3432          zfs_close(zhp);
3409      -
3410 3433          return (ret);
3411 3434  }
3412 3435  
3413 3436  /*
3414 3437   * Destroy any more recent snapshots.  We invoke this callback on any dependents
3415 3438   * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
3416 3439   * is a dependent and we should just destroy it without checking the transaction
3417 3440   * group.
3418 3441   */
3419 3442  typedef struct rollback_data {
↓ open down ↓ 7 lines elided ↑ open up ↑
3427 3450  static int
3428 3451  rollback_destroy(zfs_handle_t *zhp, void *data)
3429 3452  {
3430 3453          rollback_data_t *cbp = data;
3431 3454  
3432 3455          if (!cbp->cb_dependent) {
3433 3456                  if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
3434 3457                      zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3435 3458                      zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3436 3459                      cbp->cb_create) {
3437      -                        char *logstr;
3438 3460  
3439 3461                          cbp->cb_dependent = B_TRUE;
3440 3462                          cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
3441 3463                              rollback_destroy, cbp);
3442 3464                          cbp->cb_dependent = B_FALSE;
3443 3465  
3444      -                        logstr = zhp->zfs_hdl->libzfs_log_str;
3445      -                        zhp->zfs_hdl->libzfs_log_str = NULL;
3446 3466                          cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
3447      -                        zhp->zfs_hdl->libzfs_log_str = logstr;
3448 3467                  }
3449 3468          } else {
3450 3469                  /* We must destroy this clone; first unmount it */
3451 3470                  prop_changelist_t *clp;
3452 3471  
3453 3472                  clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3454 3473                      cbp->cb_force ? MS_FORCE: 0);
3455 3474                  if (clp == NULL || changelist_prefix(clp) != 0) {
3456 3475                          cbp->cb_error = B_TRUE;
3457 3476                          zfs_close(zhp);
↓ open down ↓ 902 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX