Illumos 4368, 4369.

4369 implement zfs bookmarks
4368 zfs send filesystems from readonly pools
Reviewed by: Christopher Siden <christopher.siden@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Approved by: Garrett D'Amore <garrett@damore.org>

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 <tim@chase2k.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #2530
This commit is contained in:
Matthew Ahrens
2013-12-11 14:33:41 -08:00
committed by Brian Behlendorf
parent b0bc7a84d9
commit da536844d5
33 changed files with 1677 additions and 270 deletions
+11 -6
View File
@@ -23,6 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
* 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--;
}
+259 -61
View File
@@ -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)
{