diff options
Diffstat (limited to 'usr/src/cmd/zfs/zfs_main.c')
-rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 320 |
1 files changed, 259 insertions, 61 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index 4cc2e537c1..e2db6b1688 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -99,6 +99,7 @@ static int zfs_do_hold(int argc, char **argv); static int zfs_do_holds(int argc, char **argv); static int zfs_do_release(int argc, char **argv); static int zfs_do_diff(int argc, char **argv); +static int zfs_do_bookmark(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -145,6 +146,7 @@ typedef enum { HELP_HOLDS, HELP_RELEASE, HELP_DIFF, + HELP_BOOKMARK, } zfs_help_t; typedef struct zfs_command { @@ -171,6 +173,7 @@ static zfs_command_t command_table[] = { { "clone", zfs_do_clone, HELP_CLONE }, { "promote", zfs_do_promote, HELP_PROMOTE }, { "rename", zfs_do_rename, HELP_RENAME }, + { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, { NULL }, { "list", zfs_do_list, HELP_LIST }, { NULL }, @@ -218,11 +221,12 @@ get_usage(zfs_help_t idx) case HELP_DESTROY: return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" "\tdestroy [-dnpRrv] " - "<filesystem|volume>@<snap>[%<snap>][,...]\n")); + "<filesystem|volume>@<snap>[%<snap>][,...]\n" + "\tdestroy <filesystem|volume>#<bookmark>\n")); case HELP_GET: return (gettext("\tget [-crHp] [-d max] " - "[-o \"all\" | field[,...]] [-t type[,...]] " - "[-s source[,...]]\n" + "[-o \"all\" | field[,...]]\n" + "\t [-t type[,...]] [-s source[,...]]\n" "\t <\"all\" | property[,...]> " "[filesystem|volume|snapshot] ...\n")); case HELP_INHERIT: @@ -248,12 +252,14 @@ get_usage(zfs_help_t idx) return (gettext("\trename [-f] <filesystem|volume|snapshot> " "<filesystem|volume|snapshot>\n" "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n" - "\trename -r <snapshot> <snapshot>")); + "\trename -r <snapshot> <snapshot>\n")); case HELP_ROLLBACK: return (gettext("\trollback [-rRf] <snapshot>\n")); case HELP_SEND: return (gettext("\tsend [-DnPpRv] [-[iI] snapshot] " - "<snapshot>\n")); + "<snapshot>\n" + "\tsend [-i snapshot|bookmark] " + "<filesystem|volume|snapshot>\n")); case HELP_SET: return (gettext("\tset <property=value> " "<filesystem|volume|snapshot> ...\n")); @@ -261,7 +267,7 @@ get_usage(zfs_help_t idx) return (gettext("\tshare <-a | filesystem>\n")); case HELP_SNAPSHOT: return (gettext("\tsnapshot [-r] [-o property=value] ... " - "<filesystem@snapname|volume@snapname> ...\n")); + "<filesystem|volume>@<snap> ...\n")); case HELP_UNMOUNT: return (gettext("\tunmount [-f] " "<-a | filesystem|mountpoint>\n")); @@ -290,11 +296,13 @@ get_usage(zfs_help_t idx) "<filesystem|volume>\n")); case HELP_USERSPACE: return (gettext("\tuserspace [-Hinp] [-o field[,...]] " - "[-s field]...\n\t [-S field]... [-t type[,...]] " + "[-s field] ...\n" + "\t [-S field] ... [-t type[,...]] " "<filesystem|snapshot>\n")); case HELP_GROUPSPACE: return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " - "[-s field]...\n\t [-S field]... [-t type[,...]] " + "[-s field] ...\n" + "\t [-S field] ... [-t type[,...]] " "<filesystem|snapshot>\n")); case HELP_HOLD: return (gettext("\thold [-r] <tag> <snapshot> ...\n")); @@ -305,6 +313,8 @@ get_usage(zfs_help_t idx) case HELP_DIFF: return (gettext("\tdiff [-FHt] <snapshot> " "[snapshot|filesystem]\n")); + case HELP_BOOKMARK: + return (gettext("\tbookmark <snapshot> <bookmark>\n")); } abort(); @@ -923,6 +933,7 @@ typedef struct destroy_cbdata { char *cb_prevsnap; int64_t cb_snapused; char *cb_snapspec; + char *cb_bookmark; } destroy_cbdata_t; /* @@ -1237,7 +1248,7 @@ zfs_do_destroy(int argc, char **argv) int err = 0; int c; zfs_handle_t *zhp = NULL; - char *at; + char *at, *pound; zfs_type_t type = ZFS_TYPE_DATASET; /* check options */ @@ -1292,6 +1303,7 @@ zfs_do_destroy(int argc, char **argv) } at = strchr(argv[0], '@'); + pound = strchr(argv[0], '#'); if (at != NULL) { /* Build the list of snaps to destroy in cb_nvl. */ @@ -1353,6 +1365,46 @@ zfs_do_destroy(int argc, char **argv) if (err != 0) rv = 1; + } else if (pound != NULL) { + int err; + nvlist_t *nvl; + + if (cb.cb_dryrun) { + (void) fprintf(stderr, + "dryrun is not supported with bookmark\n"); + return (-1); + } + + if (cb.cb_defer_destroy) { + (void) fprintf(stderr, + "defer destroy is not supported with bookmark\n"); + return (-1); + } + + if (cb.cb_recurse) { + (void) fprintf(stderr, + "recursive is not supported with bookmark\n"); + return (-1); + } + + if (!zfs_bookmark_exists(argv[0])) { + (void) fprintf(stderr, gettext("bookmark '%s' " + "does not exist.\n"), argv[0]); + return (1); + } + + nvl = fnvlist_alloc(); + fnvlist_add_boolean(nvl, argv[0]); + + err = lzc_destroy_bookmarks(nvl, NULL); + if (err != 0) { + (void) zfs_standard_error(g_zfs, err, + "cannot destroy bookmark"); + } + + nvlist_free(cb.cb_nvl); + + return (err); } else { /* Open the given dataset */ if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) @@ -1718,7 +1770,8 @@ zfs_do_get(int argc, char **argv) flags &= ~ZFS_ITER_PROP_LISTSNAPS; while (*optarg != '\0') { static char *type_subopts[] = { "filesystem", - "volume", "snapshot", "all", NULL }; + "volume", "snapshot", "bookmark", + "all", NULL }; switch (getsubopt(&optarg, type_subopts, &value)) { @@ -1732,7 +1785,11 @@ zfs_do_get(int argc, char **argv) types |= ZFS_TYPE_SNAPSHOT; break; case 3: - types = ZFS_TYPE_DATASET; + types |= ZFS_TYPE_BOOKMARK; + break; + case 4: + types = ZFS_TYPE_DATASET | + ZFS_TYPE_BOOKMARK; break; default: @@ -3064,7 +3121,8 @@ zfs_do_list(int argc, char **argv) flags &= ~ZFS_ITER_PROP_LISTSNAPS; while (*optarg != '\0') { static char *type_subopts[] = { "filesystem", - "volume", "snapshot", "all", NULL }; + "volume", "snapshot", "bookmark", + "all", NULL }; switch (getsubopt(&optarg, type_subopts, &value)) { @@ -3078,9 +3136,12 @@ zfs_do_list(int argc, char **argv) types |= ZFS_TYPE_SNAPSHOT; break; case 3: - types = ZFS_TYPE_DATASET; + types |= ZFS_TYPE_BOOKMARK; + break; + case 4: + types = ZFS_TYPE_DATASET | + ZFS_TYPE_BOOKMARK; break; - default: (void) fprintf(stderr, gettext("invalid type '%s'\n"), @@ -3295,9 +3356,29 @@ typedef struct rollback_cbdata { char *cb_target; int cb_error; boolean_t cb_recurse; - boolean_t cb_dependent; } rollback_cbdata_t; +static int +rollback_check_dependent(zfs_handle_t *zhp, void *data) +{ + rollback_cbdata_t *cbp = data; + + if (cbp->cb_first && cbp->cb_recurse) { + (void) fprintf(stderr, gettext("cannot rollback to " + "'%s': clones of previous snapshots exist\n"), + cbp->cb_target); + (void) fprintf(stderr, gettext("use '-R' to " + "force deletion of the following clones and " + "dependents:\n")); + cbp->cb_first = 0; + cbp->cb_error = 1; + } + + (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); + + zfs_close(zhp); + return (0); +} /* * Report any snapshots more recent than the one specified. Used when '-r' is * not specified. We reuse this same callback for the snapshot dependents - if @@ -3314,52 +3395,30 @@ rollback_check(zfs_handle_t *zhp, void *data) return (0); } - if (!cbp->cb_dependent) { - if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && - zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && - zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > - cbp->cb_create) { - - if (cbp->cb_first && !cbp->cb_recurse) { - (void) fprintf(stderr, gettext("cannot " - "rollback to '%s': more recent snapshots " - "exist\n"), - cbp->cb_target); - (void) fprintf(stderr, gettext("use '-r' to " - "force deletion of the following " - "snapshots:\n")); - cbp->cb_first = 0; - cbp->cb_error = 1; - } - - if (cbp->cb_recurse) { - cbp->cb_dependent = B_TRUE; - if (zfs_iter_dependents(zhp, B_TRUE, - rollback_check, cbp) != 0) { - zfs_close(zhp); - return (-1); - } - cbp->cb_dependent = B_FALSE; - } else { - (void) fprintf(stderr, "%s\n", - zfs_get_name(zhp)); - } - } - } else { - if (cbp->cb_first && cbp->cb_recurse) { - (void) fprintf(stderr, gettext("cannot rollback to " - "'%s': clones of previous snapshots exist\n"), + if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { + if (cbp->cb_first && !cbp->cb_recurse) { + (void) fprintf(stderr, gettext("cannot " + "rollback to '%s': more recent snapshots " + "or bookmarks exist\n"), cbp->cb_target); - (void) fprintf(stderr, gettext("use '-R' to " - "force deletion of the following clones and " - "dependents:\n")); + (void) fprintf(stderr, gettext("use '-r' to " + "force deletion of the following " + "snapshots and bookmarks:\n")); cbp->cb_first = 0; cbp->cb_error = 1; } - (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); + if (cbp->cb_recurse) { + if (zfs_iter_dependents(zhp, B_TRUE, + rollback_check_dependent, cbp) != 0) { + zfs_close(zhp); + return (-1); + } + } else { + (void) fprintf(stderr, "%s\n", + zfs_get_name(zhp)); + } } - zfs_close(zhp); return (0); } @@ -3429,7 +3488,9 @@ zfs_do_rollback(int argc, char **argv) cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); cb.cb_first = B_TRUE; cb.cb_error = 0; - if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) + if ((ret = zfs_iter_snapshots(zhp, rollback_check, &cb)) != 0) + goto out; + if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0) goto out; if ((ret = cb.cb_error) != 0) @@ -3724,12 +3785,45 @@ zfs_do_send(int argc, char **argv) return (1); } - cp = strchr(argv[0], '@'); - if (cp == NULL) { - (void) fprintf(stderr, - gettext("argument must be a snapshot\n")); - usage(B_FALSE); + /* + * Special case sending a filesystem, or from a bookmark. + */ + if (strchr(argv[0], '@') == NULL || + (fromname && strchr(fromname, '#') != NULL)) { + char frombuf[ZFS_MAXNAMELEN]; + + if (flags.replicate || flags.doall || flags.props || + flags.dedup || flags.dryrun || flags.verbose || + flags.progress) { + (void) fprintf(stderr, + gettext("Error: " + "Unsupported flag with filesystem or bookmark.\n")); + return (1); + } + + zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET); + if (zhp == NULL) + return (1); + + if (fromname != NULL && + (fromname[0] == '#' || fromname[0] == '@')) { + /* + * Incremental source name begins with # or @. + * Default to same fs as target. + */ + (void) strncpy(frombuf, argv[0], sizeof (frombuf)); + cp = strchr(frombuf, '@'); + if (cp != NULL) + *cp = '\0'; + (void) strlcat(frombuf, fromname, sizeof (frombuf)); + fromname = frombuf; + } + err = zfs_send_one(zhp, fromname, STDOUT_FILENO); + zfs_close(zhp); + return (err != 0); } + + cp = strchr(argv[0], '@'); *cp = '\0'; toname = cp + 1; zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); @@ -3885,6 +3979,7 @@ zfs_do_receive(int argc, char **argv) #define ZFS_DELEG_PERM_HOLD "hold" #define ZFS_DELEG_PERM_RELEASE "release" #define ZFS_DELEG_PERM_DIFF "diff" +#define ZFS_DELEG_PERM_BOOKMARK "bookmark" #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE @@ -3904,6 +3999,7 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, + { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK }, { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, @@ -6596,6 +6692,108 @@ zfs_do_diff(int argc, char **argv) return (err != 0); } +/* + * zfs bookmark <fs@snap> <fs#bmark> + * + * Creates a bookmark with the given name from the given snapshot. + */ +static int +zfs_do_bookmark(int argc, char **argv) +{ + char snapname[ZFS_MAXNAMELEN]; + zfs_handle_t *zhp; + nvlist_t *nvl; + int ret = 0; + int c; + + /* check options */ + while ((c = getopt(argc, argv, "")) != -1) { + switch (c) { + case '?': + (void) fprintf(stderr, + gettext("invalid option '%c'\n"), optopt); + goto usage; + } + } + + argc -= optind; + argv += optind; + + /* check number of arguments */ + if (argc < 1) { + (void) fprintf(stderr, gettext("missing snapshot argument\n")); + goto usage; + } + if (argc < 2) { + (void) fprintf(stderr, gettext("missing bookmark argument\n")); + goto usage; + } + + if (strchr(argv[1], '#') == NULL) { + (void) fprintf(stderr, + gettext("invalid bookmark name '%s' -- " + "must contain a '#'\n"), argv[1]); + goto usage; + } + + if (argv[0][0] == '@') { + /* + * Snapshot name begins with @. + * Default to same fs as bookmark. + */ + (void) strncpy(snapname, argv[1], sizeof (snapname)); + *strchr(snapname, '#') = '\0'; + (void) strlcat(snapname, argv[0], sizeof (snapname)); + } else { + (void) strncpy(snapname, argv[0], sizeof (snapname)); + } + zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT); + if (zhp == NULL) + goto usage; + zfs_close(zhp); + + + nvl = fnvlist_alloc(); + fnvlist_add_string(nvl, argv[1], snapname); + ret = lzc_bookmark(nvl, NULL); + fnvlist_free(nvl); + + if (ret != 0) { + const char *err_msg; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, + "cannot create bookmark '%s'"), argv[1]); + + switch (ret) { + case EXDEV: + err_msg = "bookmark is in a different pool"; + break; + case EEXIST: + err_msg = "bookmark exists"; + break; + case EINVAL: + err_msg = "invalid argument"; + break; + case ENOTSUP: + err_msg = "bookmark feature not enabled"; + break; + default: + err_msg = "unknown error"; + break; + } + (void) fprintf(stderr, "%s: %s\n", errbuf, + dgettext(TEXT_DOMAIN, err_msg)); + } + + return (ret); + +usage: + usage(B_FALSE); + return (-1); +} + int main(int argc, char **argv) { |