diff options
author | Christian Schwarz <[email protected]> | 2019-11-10 23:24:14 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2020-02-11 13:19:12 -0800 |
commit | a73f361fdb2c0a7778e70b482e316054fc2d8630 (patch) | |
tree | 094642f07952d4149c2dab358a35910961d0c42e /cmd/zfs/zfs_main.c | |
parent | 7b49bbc8164a8a5cd31cf1ba7a6cd88269fec8d0 (diff) |
Implement bookmark copying
This feature allows copying existing bookmarks using
zfs bookmark fs#target fs#newbookmark
There are some niche use cases for such functionality,
e.g. when using bookmarks as markers for replication progress.
Copying redaction bookmarks produces a normal bookmark that
cannot be used for redacted send (we are not duplicating
the redaction object).
ZCP support for bookmarking (both creation and copying) will be
implemented in a separate patch based on this work.
Overview:
- Terminology:
- source = existing snapshot or bookmark
- new/bmark = new bookmark
- Implement bookmark copying in `dsl_bookmark.c`
- create new bookmark node
- copy source's `zbn_phys` to new's `zbn_phys`
- zero-out redaction object id in copy
- Extend existing bookmark ioctl nvlist schema to accept
bookmarks as sources
- => `dsl_bookmark_create_nvl_validate` is authoritative
- use `dsl_dataset_is_before` check for both snapshot
and bookmark sources
- Adjust CLI
- refactor shortname expansion logic in `zfs_do_bookmark`
- Update man pages
- warn about redaction bookmark handling
- Add test cases
- CLI
- pyyzfs libzfs_core bindings
Reviewed-by: Matt Ahrens <[email protected]>
Reviewed-by: Paul Dagnelie <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Christian Schwarz <[email protected]>
Closes #9571
Diffstat (limited to 'cmd/zfs/zfs_main.c')
-rw-r--r-- | cmd/zfs/zfs_main.c | 99 |
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; |