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>

@@ -1405,12 +1405,11 @@
         prop_changelist_t *cl = NULL;
         char errbuf[1024];
         libzfs_handle_t *hdl = zhp->zfs_hdl;
         nvlist_t *nvl = NULL, *realprops;
         zfs_prop_t prop;
-        boolean_t do_prefix;
-        uint64_t idx;
+        boolean_t do_prefix = B_TRUE;
         int added_resv;
 
         (void) snprintf(errbuf, sizeof (errbuf),
             dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
             zhp->zfs_name);

@@ -1445,16 +1444,21 @@
                 ret = zfs_error(hdl, EZFS_ZONED, errbuf);
                 goto error;
         }
 
         /*
-         * If the dataset's canmount property is being set to noauto,
-         * then we want to prevent unmounting & remounting it.
+         * We don't want to unmount & remount the dataset when changing
+         * its canmount property to 'on' or 'noauto'.  We only use
+         * the changelist logic to unmount when setting canmount=off.
          */
-        do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
-            (zprop_string_to_index(prop, propval, &idx,
-            ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
+        if (prop == ZFS_PROP_CANMOUNT) {
+                uint64_t idx;
+                int err = zprop_string_to_index(prop, propval, &idx,
+                    ZFS_TYPE_DATASET);
+                if (err == 0 && idx != ZFS_CANMOUNT_OFF)
+                        do_prefix = B_FALSE;
+        }
 
         if (do_prefix && (ret = changelist_prefix(cl)) != 0)
                 goto error;
 
         /*

@@ -2639,29 +2643,10 @@
                 zfs_nicenum(propvalue, propbuf, proplen);
         }
         return (0);
 }
 
-int
-zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
-    uint64_t *usedp)
-{
-        int err;
-        zfs_cmd_t zc = { 0 };
-
-        (void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
-        (void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
-
-        err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
-        if (err)
-                return (err);
-
-        *usedp = zc.zc_cookie;
-
-        return (0);
-}
-
 /*
  * Returns the name of the given zfs handle.
  */
 const char *
 zfs_get_name(const zfs_handle_t *zhp)

@@ -2858,31 +2843,26 @@
          * Attempt to create, mount, and share any ancestor filesystems,
          * up to the prefixlen-long one.
          */
         for (cp = target + prefixlen + 1;
             cp = strchr(cp, '/'); *cp = '/', cp++) {
-                char *logstr;
 
                 *cp = '\0';
 
                 h = make_dataset_handle(hdl, target);
                 if (h) {
                         /* it already exists, nothing to do here */
                         zfs_close(h);
                         continue;
                 }
 
-                logstr = hdl->libzfs_log_str;
-                hdl->libzfs_log_str = NULL;
                 if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
                     NULL) != 0) {
-                        hdl->libzfs_log_str = logstr;
                         opname = dgettext(TEXT_DOMAIN, "create");
                         goto ancestorerr;
                 }
 
-                hdl->libzfs_log_str = logstr;
                 h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
                 if (h == NULL) {
                         opname = dgettext(TEXT_DOMAIN, "open");
                         goto ancestorerr;
                 }

@@ -2936,16 +2916,16 @@
  */
 int
 zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
     nvlist_t *props)
 {
-        zfs_cmd_t zc = { 0 };
         int ret;
         uint64_t size = 0;
         uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
         char errbuf[1024];
         uint64_t zoned;
+        dmu_objset_type_t ost;
 
         (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
             "cannot create '%s'"), path);
 
         /* validate the path, taking care to note the extended error message */

@@ -2961,21 +2941,20 @@
          * one that already exists is a little strange.  In particular, if you
          * try to create a dataset on top of an existing dataset, the ioctl()
          * will return ENOENT, not EEXIST.  To prevent this from happening, we
          * first try to see if the dataset exists.
          */
-        (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
-        if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
+        if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
                 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                     "dataset already exists"));
                 return (zfs_error(hdl, EZFS_EXISTS, errbuf));
         }
 
         if (type == ZFS_TYPE_VOLUME)
-                zc.zc_objset_type = DMU_OST_ZVOL;
+                ost = DMU_OST_ZVOL;
         else
-                zc.zc_objset_type = DMU_OST_ZFS;
+                ost = DMU_OST_ZFS;
 
         if (props && (props = zfs_valid_proplist(hdl, type, props,
             zoned, NULL, errbuf)) == 0)
                 return (-1);
 

@@ -3023,18 +3002,13 @@
                             "size"));
                         return (zfs_error(hdl, EZFS_BADPROP, errbuf));
                 }
         }
 
-        if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
-                return (-1);
-        nvlist_free(props);
-
         /* create the dataset */
-        ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
-
-        zcmd_free_nvlists(&zc);
+        ret = lzc_create(path, ost, props);
+        nvlist_free(props);
 
         /* check for failure */
         if (ret != 0) {
                 char parent[ZFS_MAXNAMELEN];
                 (void) parent_name(path, parent, sizeof (parent));

@@ -3168,51 +3142,51 @@
  */
 int
 zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
 {
         int ret;
-        zfs_cmd_t zc = { 0 };
+        nvlist_t *errlist;
 
-        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-        if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
-                return (-1);
-        zc.zc_defer_destroy = defer;
+        ret = lzc_destroy_snaps(snaps, defer, &errlist);
 
-        ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
         if (ret != 0) {
+                for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
+                    pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
                 char errbuf[1024];
+                        (void) snprintf(errbuf, sizeof (errbuf),
+                            dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
+                            nvpair_name(pair));
 
-                (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
-                    "cannot destroy snapshots in %s"), zc.zc_name);
-
-                switch (errno) {
+                        switch (fnvpair_value_int32(pair)) {
                 case EEXIST:
-                        zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                                zfs_error_aux(zhp->zfs_hdl,
+                                    dgettext(TEXT_DOMAIN,
                             "snapshot is cloned"));
-                        return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
-
+                                ret = zfs_error(zhp->zfs_hdl, EZFS_EXISTS,
+                                    errbuf);
+                                break;
                 default:
-                        return (zfs_standard_error(zhp->zfs_hdl, errno,
-                            errbuf));
+                                ret = zfs_standard_error(zhp->zfs_hdl, errno,
+                                    errbuf);
+                                break;
+                        }
                 }
         }
 
-        return (0);
+        return (ret);
 }
 
 /*
  * Clones the given dataset.  The target must be of the same type as the source.
  */
 int
 zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
 {
-        zfs_cmd_t zc = { 0 };
         char parent[ZFS_MAXNAMELEN];
         int ret;
         char errbuf[1024];
         libzfs_handle_t *hdl = zhp->zfs_hdl;
-        zfs_type_t type;
         uint64_t zoned;
 
         assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
 
         (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,

@@ -3227,36 +3201,25 @@
                 return (-1);
 
         (void) parent_name(target, parent, sizeof (parent));
 
         /* do the clone */
+
+        if (props) {
+                zfs_type_t type;
         if (ZFS_IS_VOLUME(zhp)) {
-                zc.zc_objset_type = DMU_OST_ZVOL;
                 type = ZFS_TYPE_VOLUME;
         } else {
-                zc.zc_objset_type = DMU_OST_ZFS;
                 type = ZFS_TYPE_FILESYSTEM;
         }
-
-        if (props) {
                 if ((props = zfs_valid_proplist(hdl, type, props, zoned,
                     zhp, errbuf)) == NULL)
                         return (-1);
-
-                if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
-                        nvlist_free(props);
-                        return (-1);
                 }
 
+        ret = lzc_clone(target, zhp->zfs_name, props);
                 nvlist_free(props);
-        }
-
-        (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
-        (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
-        ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
-
-        zcmd_free_nvlists(&zc);
 
         if (ret != 0) {
                 switch (errno) {
 
                 case ENOENT:

@@ -3337,78 +3300,138 @@
                 }
         }
         return (ret);
 }
 
+typedef struct snapdata {
+        nvlist_t *sd_nvl;
+        const char *sd_snapname;
+} snapdata_t;
+
+static int
+zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
+{
+        snapdata_t *sd = arg;
+        char name[ZFS_MAXNAMELEN];
+        int rv = 0;
+
+        (void) snprintf(name, sizeof (name),
+            "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
+
+        fnvlist_add_boolean(sd->sd_nvl, name);
+
+        rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
+        zfs_close(zhp);
+        return (rv);
+}
+
 /*
- * Takes a snapshot of the given dataset.
+ * Creates snapshots.  The keys in the snaps nvlist are the snapshots to be
+ * created.
  */
 int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
-    nvlist_t *props)
+zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
 {
-        const char *delim;
-        char parent[ZFS_MAXNAMELEN];
-        zfs_handle_t *zhp;
-        zfs_cmd_t zc = { 0 };
         int ret;
         char errbuf[1024];
+        nvpair_t *elem;
+        nvlist_t *errors;
 
         (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
-            "cannot snapshot '%s'"), path);
+            "cannot create snapshots "));
+
+        elem = NULL;
+        while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
+                const char *snapname = nvpair_name(elem);
 
         /* validate the target name */
-        if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
+                if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
+                    B_TRUE)) {
+                        (void) snprintf(errbuf, sizeof (errbuf),
+                            dgettext(TEXT_DOMAIN,
+                            "cannot create snapshot '%s'"), snapname);
                 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+                }
+        }
 
-        if (props) {
-                if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
-                    props, B_FALSE, NULL, errbuf)) == NULL)
+        if (props != NULL &&
+            (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
+            props, B_FALSE, NULL, errbuf)) == NULL) {
                         return (-1);
+        }
 
-                if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
-                        nvlist_free(props);
-                        return (-1);
+        ret = lzc_snapshot(snaps, props, &errors);
+
+        if (ret != 0) {
+                boolean_t printed = B_FALSE;
+                for (elem = nvlist_next_nvpair(errors, NULL);
+                    elem != NULL;
+                    elem = nvlist_next_nvpair(errors, elem)) {
+                        (void) snprintf(errbuf, sizeof (errbuf),
+                            dgettext(TEXT_DOMAIN,
+                            "cannot create snapshot '%s'"), nvpair_name(elem));
+                        (void) zfs_standard_error(hdl,
+                            fnvpair_value_int32(elem), errbuf);
+                        printed = B_TRUE;
                 }
+                if (!printed) {
+                        switch (ret) {
+                        case EXDEV:
+                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                    "multiple snapshots of same "
+                                    "fs not allowed"));
+                                (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
 
-                nvlist_free(props);
+                                break;
+                        default:
+                                (void) zfs_standard_error(hdl, ret, errbuf);
+                        }
+                }
         }
 
-        /* make sure the parent exists and is of the appropriate type */
-        delim = strchr(path, '@');
-        (void) strncpy(parent, path, delim - path);
-        parent[delim - path] = '\0';
+        nvlist_free(props);
+        nvlist_free(errors);
+        return (ret);
+}
+
+int
+zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
+    nvlist_t *props)
+{
+        int ret;
+        snapdata_t sd = { 0 };
+        char fsname[ZFS_MAXNAMELEN];
+        char *cp;
+        zfs_handle_t *zhp;
+        char errbuf[1024];
 
-        if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
+        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+            "cannot snapshot %s"), path);
+
+        if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
+                return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+
+        (void) strlcpy(fsname, path, sizeof (fsname));
+        cp = strchr(fsname, '@');
+        *cp = '\0';
+        sd.sd_snapname = cp + 1;
+
+        if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
             ZFS_TYPE_VOLUME)) == NULL) {
-                zcmd_free_nvlists(&zc);
                 return (-1);
         }
 
-        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-        (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
-        if (ZFS_IS_VOLUME(zhp))
-                zc.zc_objset_type = DMU_OST_ZVOL;
-        else
-                zc.zc_objset_type = DMU_OST_ZFS;
-        zc.zc_cookie = recursive;
-        ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
-
-        zcmd_free_nvlists(&zc);
-
-        /*
-         * if it was recursive, the one that actually failed will be in
-         * zc.zc_name.
-         */
-        if (ret != 0) {
-                (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
-                    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
-                (void) zfs_standard_error(hdl, errno, errbuf);
+        verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
+        if (recursive) {
+                (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
+        } else {
+                fnvlist_add_boolean(sd.sd_nvl, path);
         }
 
+        ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
+        nvlist_free(sd.sd_nvl);
         zfs_close(zhp);
-
         return (ret);
 }
 
 /*
  * Destroy any more recent snapshots.  We invoke this callback on any dependents

@@ -3432,21 +3455,17 @@
         if (!cbp->cb_dependent) {
                 if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
                     zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
                     zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
                     cbp->cb_create) {
-                        char *logstr;
 
                         cbp->cb_dependent = B_TRUE;
                         cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
                             rollback_destroy, cbp);
                         cbp->cb_dependent = B_FALSE;
 
-                        logstr = zhp->zfs_hdl->libzfs_log_str;
-                        zhp->zfs_hdl->libzfs_log_str = NULL;
                         cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
-                        zhp->zfs_hdl->libzfs_log_str = logstr;
                 }
         } else {
                 /* We must destroy this clone; first unmount it */
                 prop_changelist_t *clp;