]> granicus.if.org Git - zfs/blobdiff - cmd/zfs/zfs_main.c
Corrected "zfs list -t <type>" syntax
[zfs] / cmd / zfs / zfs_main.c
index 5753ccea0d8e4a7c61ba4f4fac696cf80b30a9e1..8e13dd715bc0afbc8fa4ab67478212abea36d649 100644 (file)
@@ -55,6 +55,7 @@
 #include <time.h>
 
 #include <libzfs.h>
+#include <libzfs_core.h>
 #include <zfs_prop.h>
 #include <zfs_deleg.h>
 #include <libuutil.h>
@@ -72,6 +73,7 @@ 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);
@@ -234,7 +236,7 @@ get_usage(zfs_help_t idx)
                return (gettext("\tlist [-rH][-d max] "
                    "[-o property[,...]] [-t type[,...]] [-s property] ...\n"
                    "\t    [-S property] ... "
-                   "[filesystem|volume|snapshot|snap] ...\n"));
+                   "[filesystem|volume|snapshot] ...\n"));
        case HELP_MOUNT:
                return (gettext("\tmount\n"
                    "\tmount [-vO] [-o opts] <-a | filesystem>\n"));
@@ -261,7 +263,7 @@ get_usage(zfs_help_t idx)
                return (gettext("\tshare <-a | filesystem>\n"));
        case HELP_SNAPSHOT:
                return (gettext("\tsnapshot|snap [-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"));
@@ -890,11 +892,12 @@ typedef struct destroy_cbdata {
        boolean_t       cb_parsable;
        boolean_t       cb_dryrun;
        nvlist_t        *cb_nvl;
+       nvlist_t        *cb_batchedsnaps;
 
        /* 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;
@@ -986,9 +989,27 @@ destroy_callback(zfs_handle_t *zhp, void *data)
                zfs_close(zhp);
                return (0);
        }
+       if (cb->cb_dryrun) {
+               zfs_close(zhp);
+               return (0);
+       }
 
-       if (!cb->cb_dryrun) {
-               if (zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
+       /*
+        * We batch up all contiguous snapshots (even of different
+        * filesystems) and destroy them with one ioctl.  We can't
+        * simply do all snap deletions and then all fs deletions,
+        * because we must delete a clone before its origin.
+        */
+       if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
+               fnvlist_add_boolean(cb->cb_batchedsnaps, name);
+       } else {
+               int error = zfs_destroy_snaps_nvl(g_zfs,
+                   cb->cb_batchedsnaps, B_FALSE);
+               fnvlist_free(cb->cb_batchedsnaps);
+               cb->cb_batchedsnaps = fnvlist_alloc();
+
+               if (error != 0 ||
+                   zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
                    zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
                        zfs_close(zhp);
                        return (-1);
@@ -1008,11 +1029,13 @@ destroy_print_cb(zfs_handle_t *zhp, void *arg)
 
        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);
@@ -1027,12 +1050,12 @@ destroy_print_cb(zfs_handle_t *zhp, void *arg)
        } 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);
@@ -1049,13 +1072,13 @@ destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *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);
@@ -1142,8 +1165,10 @@ static int
 zfs_do_destroy(int argc, char **argv)
 {
        destroy_cbdata_t cb = { 0 };
+       int rv = 0;
+       int err = 0;
        int c;
-       zfs_handle_t *zhp;
+       zfs_handle_t *zhp = NULL;
        char *at;
        zfs_type_t type = ZFS_TYPE_DATASET;
 
@@ -1197,11 +1222,9 @@ zfs_do_destroy(int argc, char **argv)
 
        at = strchr(argv[0], '@');
        if (at != NULL) {
-               int err = 0;
 
                /* Build the list of snaps to destroy in cb_nvl. */
-               if (nvlist_alloc(&cb.cb_nvl, NV_UNIQUE_NAME, 0) != 0)
-                       nomem();
+               cb.cb_nvl = fnvlist_alloc();
 
                *at = '\0';
                zhp = zfs_open(g_zfs, argv[0],
@@ -1212,17 +1235,15 @@ zfs_do_destroy(int argc, char **argv)
                cb.cb_snapspec = at + 1;
                if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
                    cb.cb_error) {
-                       zfs_close(zhp);
-                       nvlist_free(cb.cb_nvl);
-                       return (1);
+                       rv = 1;
+                       goto out;
                }
 
                if (nvlist_empty(cb.cb_nvl)) {
                        (void) fprintf(stderr, gettext("could not find any "
                            "snapshots to destroy; check snapshot names.\n"));
-                       zfs_close(zhp);
-                       nvlist_free(cb.cb_nvl);
-                       return (1);
+                       rv = 1;
+                       goto out;
                }
 
                if (cb.cb_verbose) {
@@ -1241,18 +1262,26 @@ zfs_do_destroy(int argc, char **argv)
                }
 
                if (!cb.cb_dryrun) {
-                       if (cb.cb_doclones)
+                       if (cb.cb_doclones) {
+                               cb.cb_batchedsnaps = fnvlist_alloc();
                                err = destroy_clones(&cb);
+                               if (err == 0) {
+                                       err = zfs_destroy_snaps_nvl(g_zfs,
+                                           cb.cb_batchedsnaps, B_FALSE);
+                               }
+                               if (err != 0) {
+                                       rv = 1;
+                                       goto out;
+                               }
+                       }
                        if (err == 0) {
-                               err = zfs_destroy_snaps_nvl(zhp, cb.cb_nvl,
+                               err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
                                    cb.cb_defer_destroy);
                        }
                }
 
-               zfs_close(zhp);
-               nvlist_free(cb.cb_nvl);
                if (err != 0)
-                       return (1);
+                       rv = 1;
        } else {
                /* Open the given dataset */
                if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
@@ -1273,8 +1302,8 @@ zfs_do_destroy(int argc, char **argv)
                            zfs_get_name(zhp));
                        (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
                            "to destroy the pool itself\n"), zfs_get_name(zhp));
-                       zfs_close(zhp);
-                       return (1);
+                       rv = 1;
+                       goto out;
                }
 
                /*
@@ -1284,30 +1313,42 @@ zfs_do_destroy(int argc, char **argv)
                if (!cb.cb_doclones &&
                    zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
                    &cb) != 0) {
-                       zfs_close(zhp);
-                       return (1);
+                       rv = 1;
+                       goto out;
                }
 
                if (cb.cb_error) {
-                       zfs_close(zhp);
-                       return (1);
+                       rv = 1;
+                       goto out;
                }
 
+               cb.cb_batchedsnaps = fnvlist_alloc();
                if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
                    &cb) != 0) {
-                       zfs_close(zhp);
-                       return (1);
+                       rv = 1;
+                       goto out;
                }
 
                /*
                 * Do the real thing.  The callback will close the
                 * handle regardless of whether it succeeds or not.
                 */
-               if (destroy_callback(zhp, &cb) != 0)
-                       return (1);
+               err = destroy_callback(zhp, &cb);
+               zhp = NULL;
+               if (err == 0) {
+                       err = zfs_destroy_snaps_nvl(g_zfs,
+                           cb.cb_batchedsnaps, cb.cb_defer_destroy);
+               }
+               if (err != 0)
+                       rv = 1;
        }
 
-       return (0);
+out:
+       fnvlist_free(cb.cb_batchedsnaps);
+       fnvlist_free(cb.cb_nvl);
+       if (zhp != NULL)
+               zfs_close(zhp);
+       return (rv);
 }
 
 static boolean_t
@@ -1908,9 +1949,11 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data)
                        /*
                         * 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++;
@@ -3422,6 +3465,32 @@ zfs_do_set(int argc, char **argv)
        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>
  *
@@ -3431,13 +3500,16 @@ zfs_do_set(int argc, char **argv)
 static int
 zfs_do_snapshot(int argc, char **argv)
 {
-       boolean_t recursive = B_FALSE;
        int ret = 0;
        signed 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) {
@@ -3447,7 +3519,8 @@ zfs_do_snapshot(int argc, char **argv)
                                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"),
@@ -3464,18 +3537,35 @@ zfs_do_snapshot(int argc, char **argv)
                (void) fprintf(stderr, gettext("missing snapshot argument\n"));
                goto usage;
        }
-       if (argc > 1) {
-               (void) fprintf(stderr, gettext("too many arguments\n"));
-               goto usage;
+
+       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);
@@ -5028,28 +5118,12 @@ cleanup2:
        return (error);
 }
 
-/*
- * zfs allow [-r] [-t] <tag> <snap> ...
- *
- *     -r      Recursively hold
- *     -t      Temporary hold (hidden option)
- *
- * Apply a user-hold with the given tag to the list of snapshots.
- */
 static int
 zfs_do_allow(int argc, char **argv)
 {
        return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
 }
 
-/*
- * zfs unallow [-r] [-t] <tag> <snap> ...
- *
- *     -r      Recursively hold
- *     -t      Temporary hold (hidden option)
- *
- * Apply a user-hold with the given tag to the list of snapshots.
- */
 static int
 zfs_do_unallow(int argc, char **argv)
 {
@@ -5063,7 +5137,6 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
        int i;
        const char *tag;
        boolean_t recursive = B_FALSE;
-       boolean_t temphold = B_FALSE;
        const char *opts = holding ? "rt" : "r";
        int c;
 
@@ -5073,9 +5146,6 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
                case 'r':
                        recursive = B_TRUE;
                        break;
-               case 't':
-                       temphold = B_TRUE;
-                       break;
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -5124,7 +5194,7 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
                }
                if (holding) {
                        if (zfs_hold(zhp, delim+1, tag, recursive,
-                           temphold, B_FALSE, -1, 0, 0) != 0)
+                           B_FALSE, -1) != 0)
                                ++errors;
                } else {
                        if (zfs_release(zhp, delim+1, tag, recursive) != 0)
@@ -5140,7 +5210,6 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
  * zfs hold [-r] [-t] <tag> <snap> ...
  *
  *     -r      Recursively hold
- *     -t      Temporary hold (hidden option)
  *
  * Apply a user-hold with the given tag to the list of snapshots.
  */
@@ -6384,8 +6453,7 @@ main(int argc, char **argv)
 
        mnttab_file = g_zfs->libzfs_mnttab;
 
-       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);
 
@@ -6408,6 +6476,9 @@ main(int argc, char **argv)
        }
        libzfs_fini(g_zfs);
 
+       if (ret == 0 && log_history)
+               (void) zpool_log_history(g_zfs, history_str);
+
        /*
         * The 'ZFS_ABORT' environment variable causes us to dump core on exit
         * for the purposes of running ::findleaks.