aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zfs')
-rw-r--r--cmd/zfs/zfs_main.c99
1 files changed, 64 insertions, 35 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 20ecb3031..ce8ed880c 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -30,6 +30,7 @@
* Copyright (c) 2019 Datto Inc.
* Copyright (c) 2019, loli10K <[email protected]>
* Copyright 2019 Joyent, Inc.
+ * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
*/
#include <assert.h>
@@ -383,7 +384,8 @@ get_usage(zfs_help_t idx)
return (gettext("\tdiff [-FHt] <snapshot> "
"[snapshot|filesystem]\n"));
case HELP_BOOKMARK:
- return (gettext("\tbookmark <snapshot> <bookmark>\n"));
+ return (gettext("\tbookmark <snapshot|bookmark> "
+ "<newbookmark>\n"));
case HELP_CHANNEL_PROGRAM:
return (gettext("\tprogram [-jn] [-t <instruction limit>] "
"[-m <memory limit (b)>]\n"
@@ -7535,16 +7537,17 @@ out:
}
/*
- * zfs bookmark <fs@snap> <fs#bmark>
+ * zfs bookmark <fs@source>|<fs#source> <fs#bookmark>
*
- * Creates a bookmark with the given name from the given snapshot.
+ * Creates a bookmark with the given name from the source snapshot
+ * or creates a copy of an existing source bookmark.
*/
static int
zfs_do_bookmark(int argc, char **argv)
{
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- char bookname[ZFS_MAX_DATASET_NAME_LEN];
- zfs_handle_t *zhp;
+ char *source, *bookname;
+ char expbuf[ZFS_MAX_DATASET_NAME_LEN];
+ int source_type;
nvlist_t *nvl;
int ret = 0;
int c;
@@ -7564,7 +7567,7 @@ zfs_do_bookmark(int argc, char **argv)
/* check number of arguments */
if (argc < 1) {
- (void) fprintf(stderr, gettext("missing snapshot argument\n"));
+ (void) fprintf(stderr, gettext("missing source argument\n"));
goto usage;
}
if (argc < 2) {
@@ -7572,50 +7575,72 @@ zfs_do_bookmark(int argc, char **argv)
goto usage;
}
- if (strchr(argv[0], '@') == NULL) {
+ source = argv[0];
+ bookname = argv[1];
+
+ if (strchr(source, '@') == NULL && strchr(source, '#') == NULL) {
(void) fprintf(stderr,
- gettext("invalid snapshot name '%s': "
- "must contain a '@'\n"), argv[0]);
+ gettext("invalid source name '%s': "
+ "must contain a '@' or '#'\n"), source);
goto usage;
}
- if (strchr(argv[1], '#') == NULL) {
+ if (strchr(bookname, '#') == NULL) {
(void) fprintf(stderr,
gettext("invalid bookmark name '%s': "
- "must contain a '#'\n"), argv[1]);
+ "must contain a '#'\n"), bookname);
goto usage;
}
- if (argv[0][0] == '@') {
- /*
- * Snapshot name begins with @.
- * Default to same fs as bookmark.
- */
- (void) strlcpy(snapname, argv[1], sizeof (snapname));
- *strchr(snapname, '#') = '\0';
- (void) strlcat(snapname, argv[0], sizeof (snapname));
- } else {
- (void) strlcpy(snapname, argv[0], sizeof (snapname));
+ /*
+ * expand source or bookname to full path:
+ * one of them may be specified as short name
+ */
+ {
+ char **expand;
+ char *source_short, *bookname_short;
+ source_short = strpbrk(source, "@#");
+ bookname_short = strpbrk(bookname, "#");
+ if (source_short == source &&
+ bookname_short == bookname) {
+ (void) fprintf(stderr, gettext(
+ "either source or bookmark must be specified as "
+ "full dataset paths"));
+ goto usage;
+ } else if (source_short != source &&
+ bookname_short != bookname) {
+ expand = NULL;
+ } else if (source_short != source) {
+ strlcpy(expbuf, source, sizeof (expbuf));
+ expand = &bookname;
+ } else if (bookname_short != bookname) {
+ strlcpy(expbuf, bookname, sizeof (expbuf));
+ expand = &source;
+ } else {
+ abort();
+ }
+ if (expand != NULL) {
+ *strpbrk(expbuf, "@#") = '\0'; /* dataset name in buf */
+ (void) strlcat(expbuf, *expand, sizeof (expbuf));
+ *expand = expbuf;
+ }
}
- if (argv[1][0] == '#') {
- /*
- * Bookmark name begins with #.
- * Default to same fs as snapshot.
- */
- (void) strlcpy(bookname, argv[0], sizeof (bookname));
- *strchr(bookname, '@') = '\0';
- (void) strlcat(bookname, argv[1], sizeof (bookname));
- } else {
- (void) strlcpy(bookname, argv[1], sizeof (bookname));
+
+ /* determine source type */
+ switch (*strpbrk(source, "@#")) {
+ case '@': source_type = ZFS_TYPE_SNAPSHOT; break;
+ case '#': source_type = ZFS_TYPE_BOOKMARK; break;
+ default: abort();
}
- zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
+ /* test the source exists */
+ zfs_handle_t *zhp;
+ zhp = zfs_open(g_zfs, source, source_type);
if (zhp == NULL)
goto usage;
zfs_close(zhp);
-
nvl = fnvlist_alloc();
- fnvlist_add_string(nvl, bookname, snapname);
+ fnvlist_add_string(nvl, bookname, source);
ret = lzc_bookmark(nvl, NULL);
fnvlist_free(nvl);
@@ -7631,6 +7656,10 @@ zfs_do_bookmark(int argc, char **argv)
case EXDEV:
err_msg = "bookmark is in a different pool";
break;
+ case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:
+ err_msg = "source is not an ancestor of the "
+ "new bookmark's dataset";
+ break;
case EEXIST:
err_msg = "bookmark exists";
break;