mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
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:
committed by
Brian Behlendorf
parent
b0bc7a84d9
commit
da536844d5
+11
-6
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user