summaryrefslogtreecommitdiffstats
path: root/cmd/zfs
diff options
context:
space:
mode:
authorMatthew Ahrens <[email protected]>2013-12-11 14:33:41 -0800
committerBrian Behlendorf <[email protected]>2014-07-29 10:55:29 -0700
commitda536844d55b2f3aaefdaebd36fb97bb867494aa (patch)
tree2b0b5bbad2595189476a8084e24f26e6106bbb4d /cmd/zfs
parentb0bc7a84d90dcbf5321d48c5b24ed771c5a128b0 (diff)
Illumos 4368, 4369.
4369 implement zfs bookmarks 4368 zfs send filesystems from readonly pools Reviewed by: Christopher Siden <[email protected]> Reviewed by: George Wilson <[email protected]> Approved by: Garrett D'Amore <[email protected]> References: https://www.illumos.org/issues/4369 https://www.illumos.org/issues/4368 https://github.com/illumos/illumos-gate/commit/78f1710 Ported by: Tim Chase <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #2530
Diffstat (limited to 'cmd/zfs')
-rw-r--r--cmd/zfs/zfs_iter.c17
-rw-r--r--cmd/zfs/zfs_main.c320
2 files changed, 270 insertions, 67 deletions
diff --git a/cmd/zfs/zfs_iter.c b/cmd/zfs/zfs_iter.c
index eb1d9a54e..2c16f6981 100644
--- a/cmd/zfs/zfs_iter.c
+++ b/cmd/zfs/zfs_iter.c
@@ -23,6 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek <[email protected]>.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
*/
#include <libintl.h>
@@ -71,7 +72,7 @@ uu_avl_pool_t *avl_pool;
* Include snaps if they were requested or if this a zfs list where types
* were not specified and the "listsnapshots" property is set on this pool.
*/
-static int
+static boolean_t
zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)
{
zpool_handle_t *zph;
@@ -91,8 +92,9 @@ static int
zfs_callback(zfs_handle_t *zhp, void *data)
{
callback_data_t *cb = data;
- int dontclose = 0;
- int include_snaps = zfs_include_snapshots(zhp, cb);
+ boolean_t dontclose = B_FALSE;
+ boolean_t include_snaps = zfs_include_snapshots(zhp, cb);
+ boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);
if ((zfs_get_type(zhp) & cb->cb_types) ||
((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
@@ -118,7 +120,7 @@ zfs_callback(zfs_handle_t *zhp, void *data)
}
}
uu_avl_insert(cb->cb_avl, node, idx);
- dontclose = 1;
+ dontclose = B_TRUE;
} else {
free(node);
}
@@ -133,11 +135,14 @@ zfs_callback(zfs_handle_t *zhp, void *data)
cb->cb_depth++;
if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
(void) zfs_iter_filesystems(zhp, zfs_callback, data);
- if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) {
+ if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
+ ZFS_TYPE_BOOKMARK)) == 0) && include_snaps)
(void) zfs_iter_snapshots(zhp,
(cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
data);
- }
+ if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
+ ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks)
+ (void) zfs_iter_bookmarks(zhp, zfs_callback, data);
cb->cb_depth--;
}
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 4989b3b47..521ce3c2b 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -101,6 +101,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.
@@ -147,6 +148,7 @@ typedef enum {
HELP_HOLDS,
HELP_RELEASE,
HELP_DIFF,
+ HELP_BOOKMARK,
} zfs_help_t;
typedef struct zfs_command {
@@ -173,6 +175,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 },
@@ -220,11 +223,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 [-rHp] [-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:
@@ -250,12 +254,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 [-DnPpRrv] [-[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"));
@@ -263,7 +269,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|volume>@<snap> ...\n"));
case HELP_UNMOUNT:
return (gettext("\tunmount [-f] "
"<-a | filesystem|mountpoint>\n"));
@@ -292,11 +298,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"));
@@ -307,6 +315,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();
@@ -921,6 +931,7 @@ typedef struct destroy_cbdata {
char *cb_prevsnap;
int64_t cb_snapused;
char *cb_snapspec;
+ char *cb_bookmark;
} destroy_cbdata_t;
/*
@@ -1190,7 +1201,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 */
@@ -1242,6 +1253,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. */
@@ -1303,6 +1315,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)
@@ -1665,7 +1717,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)) {
@@ -1679,7 +1732,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:
@@ -3027,7 +3084,8 @@ zfs_do_list(int argc, char **argv)
flags &= ~ZFS_ITER_PROP_LISTSNAPS;
while (*optarg != '\0') {
static char *type_subopts[] = { "filesystem",
- "volume", "snapshot", "snap", "all", NULL };
+ "volume", "snapshot", "snap", "bookmark",
+ "all", NULL };
switch (getsubopt(&optarg, type_subopts,
&value)) {
@@ -3042,9 +3100,12 @@ zfs_do_list(int argc, char **argv)
types |= ZFS_TYPE_SNAPSHOT;
break;
case 4:
- types = ZFS_TYPE_DATASET;
+ types |= ZFS_TYPE_BOOKMARK;
+ break;
+ case 5:
+ types = ZFS_TYPE_DATASET |
+ ZFS_TYPE_BOOKMARK;
break;
-
default:
(void) fprintf(stderr,
gettext("invalid type '%s'\n"),
@@ -3254,9 +3315,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
@@ -3273,52 +3354,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);
}
@@ -3388,7 +3447,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, B_FALSE, rollback_check, &cb)) != 0)
+ goto out;
+ if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
goto out;
if ((ret = cb.cb_error) != 0)
@@ -3683,12 +3744,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);
@@ -3844,6 +3938,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
@@ -3863,6 +3958,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 },
@@ -6440,6 +6536,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)
{