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>

@@ -54,10 +54,11 @@
 #include <sys/fs/zfs.h>
 #include <sys/types.h>
 #include <time.h>
 
 #include <libzfs.h>
+#include <libzfs_core.h>
 #include <zfs_prop.h>
 #include <zfs_deleg.h>
 #include <libuutil.h>
 #include <aclutils.h>
 #include <directory.h>

@@ -68,10 +69,11 @@
 
 libzfs_handle_t *g_zfs;
 
 static FILE *mnttab_file;
 static char history_str[HIS_MAX_RECORD_LEN];
+static boolean_t log_history = B_TRUE;
 
 static int zfs_do_clone(int argc, char **argv);
 static int zfs_do_create(int argc, char **argv);
 static int zfs_do_destroy(int argc, char **argv);
 static int zfs_do_get(int argc, char **argv);

@@ -257,11 +259,11 @@
                     "<filesystem|volume|snapshot> ...\n"));
         case HELP_SHARE:
                 return (gettext("\tshare <-a | filesystem>\n"));
         case HELP_SNAPSHOT:
                 return (gettext("\tsnapshot [-r] [-o property=value] ... "
-                    "<filesystem@snapname|volume@snapname>\n"));
+                    "<filesystem@snapname|volume@snapname> ...\n"));
         case HELP_UNMOUNT:
                 return (gettext("\tunmount [-f] "
                     "<-a | filesystem|mountpoint>\n"));
         case HELP_UNSHARE:
                 return (gettext("\tunshare "

@@ -886,13 +888,13 @@
         boolean_t       cb_parsable;
         boolean_t       cb_dryrun;
         nvlist_t        *cb_nvl;
 
         /* first snap in contiguous run */
-        zfs_handle_t    *cb_firstsnap;
+        char            *cb_firstsnap;
         /* previous snap in contiguous run */
-        zfs_handle_t    *cb_prevsnap;
+        char            *cb_prevsnap;
         int64_t         cb_snapused;
         char            *cb_snapspec;
 } destroy_cbdata_t;
 
 /*

@@ -1002,15 +1004,17 @@
         const char *name = zfs_get_name(zhp);
         int err = 0;
 
         if (nvlist_exists(cb->cb_nvl, name)) {
                 if (cb->cb_firstsnap == NULL)
-                        cb->cb_firstsnap = zfs_handle_dup(zhp);
+                        cb->cb_firstsnap = strdup(name);
                 if (cb->cb_prevsnap != NULL)
-                        zfs_close(cb->cb_prevsnap);
+                        free(cb->cb_prevsnap);
                 /* this snap continues the current range */
-                cb->cb_prevsnap = zfs_handle_dup(zhp);
+                cb->cb_prevsnap = strdup(name);
+                if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
+                        nomem();
                 if (cb->cb_verbose) {
                         if (cb->cb_parsable) {
                                 (void) printf("destroy\t%s\n", name);
                         } else if (cb->cb_dryrun) {
                                 (void) printf(gettext("would destroy %s\n"),

@@ -1021,16 +1025,16 @@
                         }
                 }
         } else if (cb->cb_firstsnap != NULL) {
                 /* end of this range */
                 uint64_t used = 0;
-                err = zfs_get_snapused_int(cb->cb_firstsnap,
+                err = lzc_snaprange_space(cb->cb_firstsnap,
                     cb->cb_prevsnap, &used);
                 cb->cb_snapused += used;
-                zfs_close(cb->cb_firstsnap);
+                free(cb->cb_firstsnap);
                 cb->cb_firstsnap = NULL;
-                zfs_close(cb->cb_prevsnap);
+                free(cb->cb_prevsnap);
                 cb->cb_prevsnap = NULL;
         }
         zfs_close(zhp);
         return (err);
 }

@@ -1043,17 +1047,17 @@
         assert(cb->cb_prevsnap == NULL);
         err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
         if (cb->cb_firstsnap != NULL) {
                 uint64_t used = 0;
                 if (err == 0) {
-                        err = zfs_get_snapused_int(cb->cb_firstsnap,
+                        err = lzc_snaprange_space(cb->cb_firstsnap,
                             cb->cb_prevsnap, &used);
                 }
                 cb->cb_snapused += used;
-                zfs_close(cb->cb_firstsnap);
+                free(cb->cb_firstsnap);
                 cb->cb_firstsnap = NULL;
-                zfs_close(cb->cb_prevsnap);
+                free(cb->cb_prevsnap);
                 cb->cb_prevsnap = NULL;
         }
         return (err);
 }
 

@@ -1902,13 +1906,15 @@
                     "%llu", cb->cb_version);
                 if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
                         /*
                          * If they did "zfs upgrade -a", then we could
                          * be doing ioctls to different pools.  We need
-                         * to log this history once to each pool.
+                         * to log this history once to each pool, and bypass
+                         * the normal history logging that happens in main().
                          */
-                        verify(zpool_stage_history(g_zfs, history_str) == 0);
+                        (void) zpool_log_history(g_zfs, history_str);
+                        log_history = B_FALSE;
                 }
                 if (zfs_prop_set(zhp, "version", verstr) == 0)
                         cb->cb_numupgraded++;
                 else
                         cb->cb_numfailed++;

@@ -3422,36 +3428,66 @@
             ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
 
         return (ret);
 }
 
+typedef struct snap_cbdata {
+        nvlist_t *sd_nvl;
+        boolean_t sd_recursive;
+        const char *sd_snapname;
+} snap_cbdata_t;
+
+static int
+zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
+{
+        snap_cbdata_t *sd = arg;
+        char *name;
+        int rv = 0;
+        int error;
+
+        error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
+        if (error == -1)
+                nomem();
+        fnvlist_add_boolean(sd->sd_nvl, name);
+        free(name);
+
+        if (sd->sd_recursive)
+                rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
+        zfs_close(zhp);
+        return (rv);
+}
+
 /*
  * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
  *
  * Creates a snapshot with the given name.  While functionally equivalent to
  * 'zfs create', it is a separate command to differentiate intent.
  */
 static int
 zfs_do_snapshot(int argc, char **argv)
 {
-        boolean_t recursive = B_FALSE;
         int ret = 0;
         char c;
         nvlist_t *props;
+        snap_cbdata_t sd = { 0 };
+        boolean_t multiple_snaps = B_FALSE;
 
         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
                 nomem();
+        if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
+                nomem();
 
         /* check options */
         while ((c = getopt(argc, argv, "ro:")) != -1) {
                 switch (c) {
                 case 'o':
                         if (parseprop(props))
                                 return (1);
                         break;
                 case 'r':
-                        recursive = B_TRUE;
+                        sd.sd_recursive = B_TRUE;
+                        multiple_snaps = B_TRUE;
                         break;
                 case '?':
                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                             optopt);
                         goto usage;

@@ -3464,22 +3500,39 @@
         /* check number of arguments */
         if (argc < 1) {
                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
                 goto usage;
         }
-        if (argc > 1) {
-                (void) fprintf(stderr, gettext("too many arguments\n"));
+
+        if (argc > 1)
+                multiple_snaps = B_TRUE;
+        for (; argc > 0; argc--, argv++) {
+                char *atp;
+                zfs_handle_t *zhp;
+
+                atp = strchr(argv[0], '@');
+                if (atp == NULL)
+                        goto usage;
+                *atp = '\0';
+                sd.sd_snapname = atp + 1;
+                zhp = zfs_open(g_zfs, argv[0],
+                    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+                if (zhp == NULL)
+                        goto usage;
+                if (zfs_snapshot_cb(zhp, &sd) != 0)
                 goto usage;
         }
 
-        ret = zfs_snapshot(g_zfs, argv[0], recursive, props);
+        ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
+        nvlist_free(sd.sd_nvl);
         nvlist_free(props);
-        if (ret && recursive)
+        if (ret != 0 && multiple_snaps)
                 (void) fprintf(stderr, gettext("no snapshots were created\n"));
         return (ret != 0);
 
 usage:
+        nvlist_free(sd.sd_nvl);
         nvlist_free(props);
         usage(B_FALSE);
         return (-1);
 }
 

@@ -6477,12 +6530,11 @@
                 (void) fprintf(stderr, gettext("internal error: failed to "
                     "initialize ZFS library\n"));
                 return (1);
         }
 
-        zpool_set_history_str("zfs", argc, argv, history_str);
-        verify(zpool_stage_history(g_zfs, history_str) == 0);
+        zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
 
         libzfs_print_on_error(g_zfs, B_TRUE);
 
         if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
                 (void) fprintf(stderr, gettext("internal error: unable to "

@@ -6547,10 +6599,13 @@
                 libzfs_mnttab_cache(g_zfs, B_FALSE);
         }
 
         (void) fclose(mnttab_file);
 
+        if (ret == 0 && log_history)
+                (void) zpool_log_history(g_zfs, history_str);
+
         libzfs_fini(g_zfs);
 
         /*
          * The 'ZFS_ABORT' environment variable causes us to dump core on exit
          * for the purposes of running ::findleaks.