diff options
Diffstat (limited to 'cmd/zfs/zfs_main.c')
-rw-r--r-- | cmd/zfs/zfs_main.c | 687 |
1 files changed, 351 insertions, 336 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 1fbd8bc65..353fd4fa6 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <assert.h> @@ -47,12 +46,15 @@ #include <sys/mount.h> #include <sys/stat.h> #include <sys/fs/zfs.h> +#include <sys/types.h> +#include <time.h> #include <libzfs.h> #include <libuutil.h> #include "zfs_iter.h" #include "zfs_util.h" +#include "zfs_comutil.h" libzfs_handle_t *g_zfs; @@ -157,7 +159,7 @@ static zfs_command_t command_table[] = { { "list", zfs_do_list, HELP_LIST }, { NULL }, { "set", zfs_do_set, HELP_SET }, - { "get", zfs_do_get, HELP_GET }, + { "get", zfs_do_get, HELP_GET }, { "inherit", zfs_do_inherit, HELP_INHERIT }, { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, { "userspace", zfs_do_userspace, HELP_USERSPACE }, @@ -197,16 +199,15 @@ get_usage(zfs_help_t idx) "\tcreate [-ps] [-b blocksize] [-o property=value] ... " "-V <size> <volume>\n")); case HELP_DESTROY: - return (gettext("\tdestroy [-rRf] " - "<filesystem|volume|snapshot>\n" - "\tdestroy -d [-r] <filesystem|volume|snapshot>\n")); + return (gettext("\tdestroy [-rRf] <filesystem|volume>\n" + "\tdestroy [-rRd] <snapshot>\n")); case HELP_GET: return (gettext("\tget [-rHp] [-d max] " - "[-o field[,...]] [-s source[,...]]\n" + "[-o \"all\" | field[,...]] [-s source[,...]]\n" "\t <\"all\" | property[,...]> " "[filesystem|volume|snapshot] ...\n")); case HELP_INHERIT: - return (gettext("\tinherit [-r] <property> " + return (gettext("\tinherit [-rS] <property> " "<filesystem|volume|snapshot> ...\n")); case HELP_UPGRADE: return (gettext("\tupgrade [-v]\n" @@ -222,9 +223,9 @@ get_usage(zfs_help_t idx) case HELP_PROMOTE: return (gettext("\tpromote <clone-filesystem>\n")); case HELP_RECEIVE: - return (gettext("\treceive [-vnF] <filesystem|volume|" + return (gettext("\treceive [-vnFu] <filesystem|volume|" "snapshot>\n" - "\treceive [-vnF] -d <filesystem>\n")); + "\treceive [-vnFu] [-d | -e] <filesystem>\n")); case HELP_RENAME: return (gettext("\trename <filesystem|volume|snapshot> " "<filesystem|volume|snapshot>\n" @@ -233,7 +234,7 @@ get_usage(zfs_help_t idx) case HELP_ROLLBACK: return (gettext("\trollback [-rRf] <snapshot>\n")); case HELP_SEND: - return (gettext("\tsend [-R] [-[iI] snapshot] <snapshot>\n")); + return (gettext("\tsend [-RDp] [-[iI] snapshot] <snapshot>\n")); case HELP_SET: return (gettext("\tset <property=value> " "<filesystem|volume|snapshot> ...\n")); @@ -288,22 +289,39 @@ get_usage(zfs_help_t idx) /* NOTREACHED */ } +void +nomem(void) +{ + (void) fprintf(stderr, gettext("internal error: out of memory\n")); + exit(1); +} + /* * Utility function to guarantee malloc() success. */ + void * safe_malloc(size_t size) { void *data; - if ((data = calloc(1, size)) == NULL) { - (void) fprintf(stderr, "internal error: out of memory\n"); - exit(1); - } + if ((data = calloc(1, size)) == NULL) + nomem(); return (data); } +static char * +safe_strdup(char *str) +{ + char *dupstr = strdup(str); + + if (dupstr == NULL) + nomem(); + + return (dupstr); +} + /* * Callback routine that will print out information for each of * the properties. @@ -442,11 +460,8 @@ parseprop(nvlist_t *props) "specified multiple times\n"), propname); return (-1); } - if (nvlist_add_string(props, propname, propval) != 0) { - (void) fprintf(stderr, gettext("internal " - "error: out of memory\n")); - return (-1); - } + if (nvlist_add_string(props, propname, propval) != 0) + nomem(); return (0); } @@ -471,6 +486,59 @@ parse_depth(char *opt, int *flags) return (depth); } +#define PROGRESS_DELAY 2 /* seconds */ + +static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; +static time_t pt_begin; +static char *pt_header = NULL; +static boolean_t pt_shown; + +static void +start_progress_timer(void) +{ + pt_begin = time(NULL) + PROGRESS_DELAY; + pt_shown = B_FALSE; +} + +static void +set_progress_header(char *header) +{ + assert(pt_header == NULL); + pt_header = safe_strdup(header); + if (pt_shown) { + (void) printf("%s: ", header); + (void) fflush(stdout); + } +} + +static void +update_progress(char *update) +{ + if (!pt_shown && time(NULL) > pt_begin) { + int len = strlen(update); + + (void) printf("%s: %s%*.*s", pt_header, update, len, len, + pt_reverse); + (void) fflush(stdout); + pt_shown = B_TRUE; + } else if (pt_shown) { + int len = strlen(update); + + (void) printf("%s%*.*s", update, len, len, pt_reverse); + (void) fflush(stdout); + } +} + +static void +finish_progress(char *done) +{ + if (pt_shown) { + (void) printf("%s\n", done); + (void) fflush(stdout); + } + free(pt_header); + pt_header = NULL; +} /* * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> * @@ -490,11 +558,8 @@ zfs_do_clone(int argc, char **argv) int ret; int c; - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { - (void) fprintf(stderr, gettext("internal error: " - "out of memory\n")); - return (1); - } + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) + nomem(); /* check options */ while ((c = getopt(argc, argv, "o:p")) != -1) { @@ -608,11 +673,8 @@ zfs_do_create(int argc, char **argv) uint64_t intval; int canmount; - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { - (void) fprintf(stderr, gettext("internal error: " - "out of memory\n")); - return (1); - } + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) + nomem(); /* check options */ while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) { @@ -627,12 +689,8 @@ zfs_do_create(int argc, char **argv) } if (nvlist_add_uint64(props, - zfs_prop_to_name(ZFS_PROP_VOLSIZE), - intval) != 0) { - (void) fprintf(stderr, gettext("internal " - "error: out of memory\n")); - goto error; - } + zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0) + nomem(); volsize = intval; break; case 'p': @@ -649,11 +707,8 @@ zfs_do_create(int argc, char **argv) if (nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), - intval) != 0) { - (void) fprintf(stderr, gettext("internal " - "error: out of memory\n")); - goto error; - } + intval) != 0) + nomem(); break; case 'o': if (parseprop(props)) @@ -715,15 +770,14 @@ zfs_do_create(int argc, char **argv) resv_prop = ZFS_PROP_REFRESERVATION; else resv_prop = ZFS_PROP_RESERVATION; + volsize = zvol_volsize_to_reservation(volsize, props); if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), &strval) != 0) { if (nvlist_add_uint64(props, zfs_prop_to_name(resv_prop), volsize) != 0) { - (void) fprintf(stderr, gettext("internal " - "error: out of memory\n")); nvlist_free(props); - return (1); + nomem(); } } } @@ -785,12 +839,12 @@ badusage: } /* - * zfs destroy [-rRf] <fs, snap, vol> - * zfs destroy -d [-r] <fs, snap, vol> + * zfs destroy [-rRf] <fs, vol> + * zfs destroy [-rRd] <snap> * - * -r Recursively destroy all children - * -R Recursively destroy all dependents, including clones - * -f Force unmounting of any dependents + * -r Recursively destroy all children + * -R Recursively destroy all dependents, including clones + * -f Force unmounting of any dependents * -d If we can't destroy now, mark for deferred destruction * * Destroys the given dataset. By default, it will unmount any filesystems, @@ -876,7 +930,7 @@ destroy_callback(zfs_handle_t *zhp, void *data) /* * Ignore pools (which we've already flagged as an error before getting - * here. + * here). */ if (strchr(zfs_get_name(zhp), '/') == NULL && zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { @@ -940,12 +994,14 @@ zfs_do_destroy(int argc, char **argv) int c; zfs_handle_t *zhp; char *cp; + zfs_type_t type = ZFS_TYPE_DATASET; /* check options */ while ((c = getopt(argc, argv, "dfrR")) != -1) { switch (c) { case 'd': cb.cb_defer_destroy = B_TRUE; + type = ZFS_TYPE_SNAPSHOT; break; case 'f': cb.cb_force = 1; @@ -978,9 +1034,6 @@ zfs_do_destroy(int argc, char **argv) usage(B_FALSE); } - if (cb.cb_defer_destroy && cb.cb_doclones) - usage(B_FALSE); - /* * If we are doing recursive destroy of a snapshot, then the * named snapshot may not exist. Go straight to libzfs. @@ -995,11 +1048,19 @@ zfs_do_destroy(int argc, char **argv) cp++; if (cb.cb_doclones) { + boolean_t defer = cb.cb_defer_destroy; + + /* + * Temporarily ignore the defer_destroy setting since + * it's not supported for clones. + */ + cb.cb_defer_destroy = B_FALSE; cb.cb_snapname = cp; if (destroy_snap_clones(zhp, &cb) != 0) { zfs_close(zhp); return (1); } + cb.cb_defer_destroy = defer; } ret = zfs_destroy_snaps(zhp, cp, cb.cb_defer_destroy); @@ -1012,7 +1073,7 @@ zfs_do_destroy(int argc, char **argv) } /* Open the given dataset */ - if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) + if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) return (1); cb.cb_target = zhp; @@ -1062,18 +1123,32 @@ zfs_do_destroy(int argc, char **argv) return (0); } +static boolean_t +is_recvd_column(zprop_get_cbdata_t *cbp) +{ + int i; + zfs_get_column_t col; + + for (i = 0; i < ZFS_GET_NCOLS && + (col = cbp->cb_columns[i]) != GET_COL_NONE; i++) + if (col == GET_COL_RECVD) + return (B_TRUE); + return (B_FALSE); +} + /* - * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] - * < all | property[,property]... > < fs | snap | vol > ... + * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] + * < all | property[,property]... > < fs | snap | vol > ... * * -r recurse over any child datasets * -H scripted mode. Headers are stripped, and fields are separated * by tabs instead of spaces. - * -o Set of fields to display. One of "name,property,value,source". - * Default is all four. + * -o Set of fields to display. One of "name,property,value, + * received,source". Default is "name,property,value,source". + * "all" is an alias for all five. * -s Set of sources to allow. One of - * "local,default,inherited,temporary,none". Default is all - * five. + * "local,default,inherited,received,temporary,none". Default is + * all six. * -p Display values in parsable (literal) format. * * Prints properties for the given datasets. The user can control which @@ -1087,16 +1162,19 @@ static int get_callback(zfs_handle_t *zhp, void *data) { char buf[ZFS_MAXPROPLEN]; + char rbuf[ZFS_MAXPROPLEN]; zprop_source_t sourcetype; char source[ZFS_MAXNAMELEN]; zprop_get_cbdata_t *cbp = data; - nvlist_t *userprop = zfs_get_user_props(zhp); + nvlist_t *user_props = zfs_get_user_props(zhp); zprop_list_t *pl = cbp->cb_proplist; nvlist_t *propval; char *strval; char *sourceval; + boolean_t received = is_recvd_column(cbp); for (; pl != NULL; pl = pl->pl_next) { + char *recvdval = NULL; /* * Skip the special fake placeholder. This will also skip over * the name property when 'all' is specified. @@ -1123,9 +1201,14 @@ get_callback(zfs_handle_t *zhp, void *data) (void) strlcpy(buf, "-", sizeof (buf)); } + if (received && (zfs_prop_get_recvd(zhp, + zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf), + cbp->cb_literal) == 0)) + recvdval = rbuf; + zprop_print_one_property(zfs_get_name(zhp), cbp, zfs_prop_to_name(pl->pl_prop), - buf, sourcetype, source); + buf, sourcetype, source, recvdval); } else if (zfs_prop_userquota(pl->pl_user_prop)) { sourcetype = ZPROP_SRC_LOCAL; @@ -1136,9 +1219,9 @@ get_callback(zfs_handle_t *zhp, void *data) } zprop_print_one_property(zfs_get_name(zhp), cbp, - pl->pl_user_prop, buf, sourcetype, source); + pl->pl_user_prop, buf, sourcetype, source, NULL); } else { - if (nvlist_lookup_nvlist(userprop, + if (nvlist_lookup_nvlist(user_props, pl->pl_user_prop, &propval) != 0) { if (pl->pl_all) continue; @@ -1153,6 +1236,9 @@ get_callback(zfs_handle_t *zhp, void *data) if (strcmp(sourceval, zfs_get_name(zhp)) == 0) { sourcetype = ZPROP_SRC_LOCAL; + } else if (strcmp(sourceval, + ZPROP_SOURCE_VAL_RECVD) == 0) { + sourcetype = ZPROP_SRC_RECEIVED; } else { sourcetype = ZPROP_SRC_INHERITED; (void) strlcpy(source, @@ -1160,9 +1246,14 @@ get_callback(zfs_handle_t *zhp, void *data) } } + if (received && (zfs_prop_get_recvd(zhp, + pl->pl_user_prop, rbuf, sizeof (rbuf), + cbp->cb_literal) == 0)) + recvdval = rbuf; + zprop_print_one_property(zfs_get_name(zhp), cbp, pl->pl_user_prop, strval, sourcetype, - source); + source, recvdval); } } @@ -1218,10 +1309,10 @@ zfs_do_get(int argc, char **argv) i = 0; while (*optarg != '\0') { static char *col_subopts[] = - { "name", "property", "value", "source", - NULL }; + { "name", "property", "value", "received", + "source", "all", NULL }; - if (i == 4) { + if (i == ZFS_GET_NCOLS) { (void) fprintf(stderr, gettext("too " "many fields given to -o " "option\n")); @@ -1240,8 +1331,28 @@ zfs_do_get(int argc, char **argv) cb.cb_columns[i++] = GET_COL_VALUE; break; case 3: + cb.cb_columns[i++] = GET_COL_RECVD; + flags |= ZFS_ITER_RECVD_PROPS; + break; + case 4: cb.cb_columns[i++] = GET_COL_SOURCE; break; + case 5: + if (i > 0) { + (void) fprintf(stderr, + gettext("\"all\" conflicts " + "with specific fields " + "given to -o option\n")); + usage(B_FALSE); + } + cb.cb_columns[0] = GET_COL_NAME; + cb.cb_columns[1] = GET_COL_PROPERTY; + cb.cb_columns[2] = GET_COL_VALUE; + cb.cb_columns[3] = GET_COL_RECVD; + cb.cb_columns[4] = GET_COL_SOURCE; + flags |= ZFS_ITER_RECVD_PROPS; + i = ZFS_GET_NCOLS; + break; default: (void) fprintf(stderr, gettext("invalid column name " @@ -1256,7 +1367,8 @@ zfs_do_get(int argc, char **argv) while (*optarg != '\0') { static char *source_subopts[] = { "local", "default", "inherited", - "temporary", "none", NULL }; + "received", "temporary", "none", + NULL }; switch (getsubopt(&optarg, source_subopts, &value)) { @@ -1270,9 +1382,12 @@ zfs_do_get(int argc, char **argv) cb.cb_sources |= ZPROP_SRC_INHERITED; break; case 3: - cb.cb_sources |= ZPROP_SRC_TEMPORARY; + cb.cb_sources |= ZPROP_SRC_RECEIVED; break; case 4: + cb.cb_sources |= ZPROP_SRC_TEMPORARY; + break; + case 5: cb.cb_sources |= ZPROP_SRC_NONE; break; default: @@ -1339,9 +1454,10 @@ zfs_do_get(int argc, char **argv) } /* - * inherit [-r] <property> <fs|vol> ... + * inherit [-rS] <property> <fs|vol> ... * - * -r Recurse over all children + * -r Recurse over all children + * -S Revert to received value, if any * * For each dataset specified on the command line, inherit the given property * from its parent. Inheriting a property at the pool level will cause it to @@ -1350,11 +1466,16 @@ zfs_do_get(int argc, char **argv) * local modifications for each dataset. */ +typedef struct inherit_cbdata { + const char *cb_propname; + boolean_t cb_received; +} inherit_cbdata_t; + static int inherit_recurse_cb(zfs_handle_t *zhp, void *data) { - char *propname = data; - zfs_prop_t prop = zfs_name_to_prop(propname); + inherit_cbdata_t *cb = data; + zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname); /* * If we're doing it recursively, then ignore properties that @@ -1364,15 +1485,15 @@ inherit_recurse_cb(zfs_handle_t *zhp, void *data) !zfs_prop_valid_for_type(prop, zfs_get_type(zhp))) return (0); - return (zfs_prop_inherit(zhp, propname) != 0); + return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); } static int inherit_cb(zfs_handle_t *zhp, void *data) { - char *propname = data; + inherit_cbdata_t *cb = data; - return (zfs_prop_inherit(zhp, propname) != 0); + return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); } static int @@ -1380,16 +1501,21 @@ zfs_do_inherit(int argc, char **argv) { int c; zfs_prop_t prop; + inherit_cbdata_t cb = { 0 }; char *propname; int ret; int flags = 0; + boolean_t received = B_FALSE; /* check options */ - while ((c = getopt(argc, argv, "r")) != -1) { + while ((c = getopt(argc, argv, "rS")) != -1) { switch (c) { case 'r': flags |= ZFS_ITER_RECURSE; break; + case 'S': + received = B_TRUE; + break; case '?': default: (void) fprintf(stderr, gettext("invalid option '%c'\n"), @@ -1422,7 +1548,7 @@ zfs_do_inherit(int argc, char **argv) propname); return (1); } - if (!zfs_prop_inheritable(prop)) { + if (!zfs_prop_inheritable(prop) && !received) { (void) fprintf(stderr, gettext("'%s' property cannot " "be inherited\n"), propname); if (prop == ZFS_PROP_QUOTA || @@ -1433,18 +1559,27 @@ zfs_do_inherit(int argc, char **argv) "%s=none' to clear\n"), propname); return (1); } + if (received && (prop == ZFS_PROP_VOLSIZE || + prop == ZFS_PROP_VERSION)) { + (void) fprintf(stderr, gettext("'%s' property cannot " + "be reverted to a received value\n"), propname); + return (1); + } } else if (!zfs_prop_user(propname)) { (void) fprintf(stderr, gettext("invalid property '%s'\n"), propname); usage(B_FALSE); } + cb.cb_propname = propname; + cb.cb_received = received; + if (flags & ZFS_ITER_RECURSE) { ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, - NULL, NULL, 0, inherit_recurse_cb, propname); + NULL, NULL, 0, inherit_recurse_cb, &cb); } else { ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, - NULL, NULL, 0, inherit_cb, propname); + NULL, NULL, 0, inherit_cb, &cb); } return (ret); @@ -1513,31 +1648,25 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data) { upgrade_cbdata_t *cb = data; int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); - int i; - static struct { int zplver; int spaver; } table[] = { - {ZPL_VERSION_FUID, SPA_VERSION_FUID}, - {ZPL_VERSION_USERSPACE, SPA_VERSION_USERSPACE}, - {0, 0} - }; - - - for (i = 0; table[i].zplver; i++) { - if (cb->cb_version >= table[i].zplver) { - int spa_version; - - if (zfs_spa_version(zhp, &spa_version) < 0) - return (-1); - - if (spa_version < table[i].spaver) { - /* can't upgrade */ - (void) printf(gettext("%s: can not be " - "upgraded; the pool version needs to first " - "be upgraded\nto version %d\n\n"), - zfs_get_name(zhp), table[i].spaver); - cb->cb_numfailed++; - return (0); - } - } + int needed_spa_version; + int spa_version; + + if (zfs_spa_version(zhp, &spa_version) < 0) + return (-1); + + needed_spa_version = zfs_spa_version_map(cb->cb_version); + + if (needed_spa_version < 0) + return (-1); + + if (spa_version < needed_spa_version) { + /* can't upgrade */ + (void) printf(gettext("%s: can not be " + "upgraded; the pool version needs to first " + "be upgraded\nto version %d\n\n"), + zfs_get_name(zhp), needed_spa_version); + cb->cb_numfailed++; + return (0); } /* upgrade */ @@ -1639,11 +1768,10 @@ zfs_do_upgrade(int argc, char **argv) "unique identifier (FUID)\n")); (void) printf(gettext(" 4 userquota, groupquota " "properties\n")); + (void) printf(gettext(" 5 System attributes\n")); (void) printf(gettext("\nFor more information on a particular " - "version, including supported releases, see:\n\n")); - (void) printf("http://www.opensolaris.org/os/community/zfs/" - "version/zpl/N\n\n"); - (void) printf(gettext("Where 'N' is the version number.\n")); + "version, including supported releases,\n")); + (void) printf("see the ZFS Administration Guide.\n\n"); ret = 0; } else if (argc || all) { /* Upgrade filesystems */ @@ -1769,11 +1897,11 @@ zfs_do_userspace(int argc, char **argv) * [-s property [-s property]...] [-S property [-S property]...] * <dataset> ... * - * -r Recurse over all children - * -d Limit recursion by depth. - * -H Scripted mode; elide headers and separate columns by tabs - * -o Control which fields to display. - * -t Control which object types to display. + * -r Recurse over all children + * -d Limit recursion by depth. + * -H Scripted mode; elide headers and separate columns by tabs + * -o Control which fields to display. + * -t Control which object types to display. * -s Specify sort columns, descending order. * -S Specify sort columns, ascending order. * @@ -2170,9 +2298,9 @@ zfs_do_promote(int argc, char **argv) /* * zfs rollback [-rRf] <snapshot> * - * -r Delete any intervening snapshots before doing rollback - * -R Delete any snapshots and their clones - * -f ignored for backwards compatability + * -r Delete any intervening snapshots before doing rollback + * -R Delete any snapshots and their clones + * -f ignored for backwards compatability * * Given a filesystem, rollback to a specific snapshot, discarding any changes * since then and making it the active dataset. If more recent snapshots exist, @@ -2433,11 +2561,8 @@ zfs_do_snapshot(int argc, char **argv) char c; nvlist_t *props; - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { - (void) fprintf(stderr, gettext("internal error: " - "out of memory\n")); - return (1); - } + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) + nomem(); /* check options */ while ((c = getopt(argc, argv, "ro:")) != -1) { @@ -2482,8 +2607,8 @@ usage: } /* - * zfs send [-v] -R [-i|-I <@snap>] <fs@snap> - * zfs send [-v] [-i|-I <@snap>] <fs@snap> + * zfs send [-vDp] -R [-i|-I <@snap>] <fs@snap> + * zfs send [-vDp] [-i|-I <@snap>] <fs@snap> * * Send a backup stream to stdout. */ @@ -2494,14 +2619,13 @@ zfs_do_send(int argc, char **argv) char *toname = NULL; char *cp; zfs_handle_t *zhp; - boolean_t doall = B_FALSE; - boolean_t replicate = B_FALSE; - boolean_t fromorigin = B_FALSE; - boolean_t verbose = B_FALSE; + sendflags_t flags = { 0 }; int c, err; + nvlist_t *dbgnv; + boolean_t extraverbose = B_FALSE; /* check options */ - while ((c = getopt(argc, argv, ":i:I:Rv")) != -1) { + while ((c = getopt(argc, argv, ":i:I:RDpv")) != -1) { switch (c) { case 'i': if (fromname) @@ -2512,13 +2636,21 @@ zfs_do_send(int argc, char **argv) if (fromname) usage(B_FALSE); fromname = optarg; - doall = B_TRUE; + flags.doall = B_TRUE; break; case 'R': - replicate = B_TRUE; + flags.replicate = B_TRUE; + break; + case 'p': + flags.props = B_TRUE; break; case 'v': - verbose = B_TRUE; + if (flags.verbose) + extraverbose = B_TRUE; + flags.verbose = B_TRUE; + break; + case 'D': + flags.dedup = B_TRUE; break; case ':': (void) fprintf(stderr, gettext("missing argument for " @@ -2578,7 +2710,7 @@ zfs_do_send(int argc, char **argv) if (strcmp(origin, fromname) == 0) { fromname = NULL; - fromorigin = B_TRUE; + flags.fromorigin = B_TRUE; } else { *cp = '\0'; if (cp != fromname && strcmp(argv[0], fromname)) { @@ -2596,18 +2728,29 @@ zfs_do_send(int argc, char **argv) } } - if (replicate && fromname == NULL) - doall = B_TRUE; + if (flags.replicate && fromname == NULL) + flags.doall = B_TRUE; + + err = zfs_send(zhp, fromname, toname, flags, STDOUT_FILENO, NULL, 0, + extraverbose ? &dbgnv : NULL); - err = zfs_send(zhp, fromname, toname, replicate, doall, fromorigin, - verbose, STDOUT_FILENO); + if (extraverbose) { + /* + * dump_nvlist prints to stdout, but that's been + * redirected to a file. Make it print to stderr + * instead. + */ + (void) dup2(STDERR_FILENO, STDOUT_FILENO); + dump_nvlist(dbgnv, 0); + nvlist_free(dbgnv); + } zfs_close(zhp); return (err != 0); } /* - * zfs receive [-dnvF] <fs@snap> + * zfs receive [-vnFu] [-d | -e] <fs@snap> * * Restore a backup stream from stdin. */ @@ -2615,15 +2758,18 @@ static int zfs_do_receive(int argc, char **argv) { int c, err; - recvflags_t flags; + recvflags_t flags = { 0 }; - bzero(&flags, sizeof (recvflags_t)); /* check options */ - while ((c = getopt(argc, argv, ":dnuvF")) != -1) { + while ((c = getopt(argc, argv, ":denuvF")) != -1) { switch (c) { case 'd': flags.isprefix = B_TRUE; break; + case 'e': + flags.isprefix = B_TRUE; + flags.istail = B_TRUE; + break; case 'n': flags.dryrun = B_TRUE; break; @@ -2681,15 +2827,19 @@ 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; - int (*func)(zfs_handle_t *, const char *, const char *, boolean_t); /* check options */ - while ((c = getopt(argc, argv, "r")) != -1) { + while ((c = getopt(argc, argv, opts)) != -1) { switch (c) { case 'r': recursive = B_TRUE; break; + case 't': + temphold = B_TRUE; + break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); @@ -2708,16 +2858,10 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) --argc; ++argv; - if (holding) { - if (tag[0] == '.') { - /* tags starting with '.' are reserved for libzfs */ - (void) fprintf(stderr, - gettext("tag may not start with '.'\n")); - usage(B_FALSE); - } - func = zfs_hold; - } else { - func = zfs_release; + if (holding && tag[0] == '.') { + /* tags starting with '.' are reserved for libzfs */ + (void) fprintf(stderr, gettext("tag may not start with '.'\n")); + usage(B_FALSE); } for (i = 0; i < argc; ++i) { @@ -2742,8 +2886,14 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) ++errors; continue; } - if (func(zhp, delim+1, tag, recursive) != 0) - ++errors; + if (holding) { + if (zfs_hold(zhp, delim+1, tag, recursive, + temphold, B_FALSE) != 0) + ++errors; + } else { + if (zfs_release(zhp, delim+1, tag, recursive) != 0) + ++errors; + } zfs_close(zhp); } @@ -2751,9 +2901,10 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) } /* - * zfs hold [-r] <tag> <snap> ... + * zfs hold [-r] [-t] <tag> <snap> ... * - * -r Recursively hold + * -r Recursively hold + * -t Temporary hold (hidden option) * * Apply a user-hold with the given tag to the list of snapshots. */ @@ -2766,7 +2917,7 @@ zfs_do_hold(int argc, char **argv) /* * zfs release [-r] <tag> <snap> ... * - * -r Recursively release + * -r Recursively release * * Release a user-hold with the given tag from the list of snapshots. */ @@ -2791,7 +2942,7 @@ typedef struct get_all_cbdata { static int get_one_dataset(zfs_handle_t *zhp, void *data) { - static char spin[] = { '-', '\\', '|', '/' }; + static char *spin[] = { "-", "\\", "|", "/" }; static int spinval = 0; static int spincheck = 0; static time_t last_spin_time = (time_t)0; @@ -2802,8 +2953,7 @@ get_one_dataset(zfs_handle_t *zhp, void *data) if (--spincheck < 0) { time_t now = time(NULL); if (last_spin_time + SPINNER_TIME < now) { - (void) printf("\b%c", spin[spinval++ % 4]); - (void) fflush(stdout); + update_progress(spin[spinval++ % 4]); last_spin_time = now; } spincheck = CHECK_SPINNER; @@ -2859,19 +3009,16 @@ get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count, cb.cb_types = types; cb.cb_verbose = verbose; - if (verbose) { - (void) printf("%s: *", gettext("Reading ZFS config")); - (void) fflush(stdout); - } + if (verbose) + set_progress_header(gettext("Reading ZFS config")); (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); *dslist = cb.cb_handles; *count = cb.cb_used; - if (verbose) { - (void) printf("\b%s\n", gettext("done.")); - } + if (verbose) + finish_progress(gettext("done.")); } static int @@ -3098,42 +3245,9 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, return (1); break; } - } else { + } else assert(op == OP_SHARE); - /* - * Ignore any volumes that aren't shared. - */ - verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, - sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); - - if (strcmp(shareopts, "off") == 0) { - if (!explicit) - return (0); - - (void) fprintf(stderr, gettext("cannot share '%s': " - "'shareiscsi' property not set\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("set 'shareiscsi' " - "property or use iscsitadm(1M) to share this " - "volume\n")); - return (1); - } - - if (zfs_is_shared_iscsi(zhp)) { - if (!explicit) - return (0); - - (void) fprintf(stderr, gettext("cannot share " - "'%s': volume already shared\n"), - zfs_get_name(zhp)); - return (1); - } - - if (zfs_share_iscsi(zhp) != 0) - return (1); - } - return (0); } @@ -3143,19 +3257,16 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, static void report_mount_progress(int current, int total) { - static int len; - static char *reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" - "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; - static time_t last_progress_time; + static time_t last_progress_time = 0; time_t now = time(NULL); + char info[32]; /* report 1..n instead of 0..n-1 */ ++current; /* display header if we're here for the first time */ if (current == 1) { - (void) printf(gettext("Mounting ZFS filesystems: ")); - len = 0; + set_progress_header(gettext("Mounting ZFS filesystems")); } else if (current != total && last_progress_time + MOUNT_TIME >= now) { /* too soon to report again */ return; @@ -3163,13 +3274,12 @@ report_mount_progress(int current, int total) last_progress_time = now; - /* back up to prepare for overwriting */ - if (len) - (void) printf("%*.*s", len, len, reverse); + (void) sprintf(info, "(%d/%d)", current, total); - /* We put a newline at the end if this is the last one. */ - len = printf("(%d/%d)%s", current, total, current == total ? "\n" : ""); - (void) fflush(stdout); + if (current == total) + finish_progress(info); + else + update_progress(info); } static void @@ -3254,11 +3364,9 @@ share_mount(int op, int argc, char **argv) if (strcmp(argv[0], "nfs") == 0 || strcmp(argv[0], "smb") == 0) { types = ZFS_TYPE_FILESYSTEM; - } else if (strcmp(argv[0], "iscsi") == 0) { - types = ZFS_TYPE_VOLUME; } else { (void) fprintf(stderr, gettext("share type " - "must be 'nfs', 'smb' or 'iscsi'\n")); + "must be 'nfs' or 'smb'\n")); usage(B_FALSE); } protocol = argv[0]; @@ -3273,6 +3381,7 @@ share_mount(int op, int argc, char **argv) usage(B_FALSE); } + start_progress_timer(); get_all_datasets(types, &dslist, &count, verbose); if (count == 0) @@ -3341,7 +3450,7 @@ share_mount(int op, int argc, char **argv) } /* - * zfs mount -a [nfs | iscsi] + * zfs mount -a [nfs] * zfs mount filesystem * * Mount all filesystems, or mount the given filesystem. @@ -3353,7 +3462,7 @@ zfs_do_mount(int argc, char **argv) } /* - * zfs share -a [nfs | iscsi | smb] + * zfs share -a [nfs | smb] * zfs share filesystem * * Share all filesystems, or share the given filesystem. @@ -3509,7 +3618,7 @@ unshare_unmount(int op, int argc, char **argv) int ret = 0; int types, c; zfs_handle_t *zhp; - char nfsiscsi_mnt_prop[ZFS_MAXPROPLEN]; + char nfs_mnt_prop[ZFS_MAXPROPLEN]; char sharesmb[ZFS_MAXPROPLEN]; /* check options */ @@ -3558,21 +3667,12 @@ unshare_unmount(int op, int argc, char **argv) usage(B_FALSE); } - if ((pool = uu_avl_pool_create("unmount_pool", + if (((pool = uu_avl_pool_create("unmount_pool", sizeof (unshare_unmount_node_t), offsetof(unshare_unmount_node_t, un_avlnode), - unshare_unmount_compare, - UU_DEFAULT)) == NULL) { - (void) fprintf(stderr, gettext("internal error: " - "out of memory\n")); - exit(1); - } - - if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { - (void) fprintf(stderr, gettext("internal error: " - "out of memory\n")); - exit(1); - } + unshare_unmount_compare, UU_DEFAULT)) == NULL) || + ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL)) + nomem(); rewind(mnttab_file); while (getmntent(mnttab_file, &entry) == 0) { @@ -3594,25 +3694,25 @@ unshare_unmount(int op, int argc, char **argv) switch (op) { case OP_SHARE: verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, - nfsiscsi_mnt_prop, - sizeof (nfsiscsi_mnt_prop), + nfs_mnt_prop, + sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); - if (strcmp(nfsiscsi_mnt_prop, "off") != 0) + if (strcmp(nfs_mnt_prop, "off") != 0) break; verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, - nfsiscsi_mnt_prop, - sizeof (nfsiscsi_mnt_prop), + nfs_mnt_prop, + sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); - if (strcmp(nfsiscsi_mnt_prop, "off") == 0) + if (strcmp(nfs_mnt_prop, "off") == 0) continue; break; case OP_MOUNT: /* Ignore legacy mounts */ verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, - nfsiscsi_mnt_prop, - sizeof (nfsiscsi_mnt_prop), + nfs_mnt_prop, + sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); - if (strcmp(nfsiscsi_mnt_prop, "legacy") == 0) + if (strcmp(nfs_mnt_prop, "legacy") == 0) continue; /* Ignore canmount=noauto mounts */ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == @@ -3624,13 +3724,7 @@ unshare_unmount(int op, int argc, char **argv) node = safe_malloc(sizeof (unshare_unmount_node_t)); node->un_zhp = zhp; - - if ((node->un_mountp = strdup(entry.mnt_mountp)) == - NULL) { - (void) fprintf(stderr, gettext("internal error:" - " out of memory\n")); - exit(1); - } + node->un_mountp = safe_strdup(entry.mnt_mountp); uu_avl_node_init(node, &node->un_avlnode, pool); @@ -3648,11 +3742,8 @@ unshare_unmount(int op, int argc, char **argv) * removing it from the AVL tree in the process. */ if ((walk = uu_avl_walk_start(tree, - UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { - (void) fprintf(stderr, - gettext("internal error: out of memory")); - exit(1); - } + UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) + nomem(); while ((node = uu_avl_walk_next(walk)) != NULL) { uu_avl_remove(tree, node); @@ -3680,29 +3771,6 @@ unshare_unmount(int op, int argc, char **argv) uu_avl_destroy(tree); uu_avl_pool_destroy(pool); - if (op == OP_SHARE) { - /* - * Finally, unshare any volumes shared via iSCSI. - */ - zfs_handle_t **dslist = NULL; - size_t i, count = 0; - - get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count, - B_FALSE); - - if (count != 0) { - qsort(dslist, count, sizeof (void *), - dataset_cmp); - - for (i = 0; i < count; i++) { - if (zfs_unshare_iscsi(dslist[i]) != 0) - ret = 1; - zfs_close(dslist[i]); - } - - free(dslist); - } - } } else { if (argc != 1) { if (argc == 0) @@ -3734,20 +3802,20 @@ unshare_unmount(int op, int argc, char **argv) if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { verify(zfs_prop_get(zhp, op == OP_SHARE ? ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, - nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop), NULL, + nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); switch (op) { case OP_SHARE: verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, - nfsiscsi_mnt_prop, - sizeof (nfsiscsi_mnt_prop), + nfs_mnt_prop, + sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, sharesmb, sizeof (sharesmb), NULL, NULL, 0, B_FALSE) == 0); - if (strcmp(nfsiscsi_mnt_prop, "off") == 0 && + if (strcmp(nfs_mnt_prop, "off") == 0 && strcmp(sharesmb, "off") == 0) { (void) fprintf(stderr, gettext("cannot " "unshare '%s': legacy share\n"), @@ -3767,7 +3835,7 @@ unshare_unmount(int op, int argc, char **argv) break; case OP_MOUNT: - if (strcmp(nfsiscsi_mnt_prop, "legacy") == 0) { + if (strcmp(nfs_mnt_prop, "legacy") == 0) { (void) fprintf(stderr, gettext("cannot " "unmount '%s': legacy " "mountpoint\n"), zfs_get_name(zhp)); @@ -3786,29 +3854,6 @@ unshare_unmount(int op, int argc, char **argv) } break; } - } else { - assert(op == OP_SHARE); - - verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, - nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop), - NULL, NULL, 0, B_FALSE) == 0); - - if (strcmp(nfsiscsi_mnt_prop, "off") == 0) { - (void) fprintf(stderr, gettext("cannot unshare " - "'%s': 'shareiscsi' property not set\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("set " - "'shareiscsi' property or use " - "iscsitadm(1M) to share this volume\n")); - ret = 1; - } else if (!zfs_is_shared_iscsi(zhp)) { - (void) fprintf(stderr, gettext("cannot " - "unshare '%s': not currently shared\n"), - zfs_get_name(zhp)); - ret = 1; - } else if (zfs_unshare_iscsi(zhp) != 0) { - ret = 1; - } } zfs_close(zhp); @@ -3986,27 +4031,6 @@ manual_unmount(int argc, char **argv) } static int -volcheck(zpool_handle_t *zhp, void *data) -{ - boolean_t isinit = *((boolean_t *)data); - - if (isinit) - return (zpool_create_zvol_links(zhp)); - else - return (zpool_remove_zvol_links(zhp)); -} - -/* - * Iterate over all pools in the system and either create or destroy /dev/zvol - * links, depending on the value of 'isinit'. - */ -static int -do_volcheck(boolean_t isinit) -{ - return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0); -} - -static int find_command_idx(char *command, int *idx) { int i; @@ -4092,15 +4116,6 @@ main(int argc, char **argv) usage(B_TRUE); /* - * 'volinit' and 'volfini' do not appear in the usage message, - * so we have to special case them here. - */ - if (strcmp(cmdname, "volinit") == 0) - return (do_volcheck(B_TRUE)); - else if (strcmp(cmdname, "volfini") == 0) - return (do_volcheck(B_FALSE)); - - /* * Run the appropriate command. */ libzfs_mnttab_cache(g_zfs, B_TRUE); |