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 <matt@delphix.com>
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Christian Schwarz <me@cschwarz.com>
Closes #9571
This commit is contained in:
Christian Schwarz
2019-11-10 23:24:14 -08:00
committed by Brian Behlendorf
parent 7b49bbc816
commit a73f361fdb
19 changed files with 672 additions and 142 deletions
+42
View File
@@ -183,6 +183,8 @@ entity_namecheck(const char *path, namecheck_err_t *why, char *what)
{
const char *end;
EQUIV(why == NULL, what == NULL);
/*
* Make sure the name is not too long.
*/
@@ -310,6 +312,44 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
return (ret);
}
/*
* Assert path is a valid bookmark name
*/
int
bookmark_namecheck(const char *path, namecheck_err_t *why, char *what)
{
int ret = entity_namecheck(path, why, what);
if (ret == 0 && strchr(path, '#') == NULL) {
if (why != NULL) {
*why = NAME_ERR_NO_POUND;
*what = '#';
}
return (-1);
}
return (ret);
}
/*
* Assert path is a valid snapshot name
*/
int
snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
{
int ret = entity_namecheck(path, why, what);
if (ret == 0 && strchr(path, '@') == NULL) {
if (why != NULL) {
*why = NAME_ERR_NO_AT;
*what = '@';
}
return (-1);
}
return (ret);
}
/*
* mountpoint names must be of the following form:
*
@@ -420,6 +460,8 @@ pool_namecheck(const char *pool, namecheck_err_t *why, char *what)
EXPORT_SYMBOL(entity_namecheck);
EXPORT_SYMBOL(pool_namecheck);
EXPORT_SYMBOL(dataset_namecheck);
EXPORT_SYMBOL(bookmark_namecheck);
EXPORT_SYMBOL(snapshot_namecheck);
EXPORT_SYMBOL(zfs_component_namecheck);
EXPORT_SYMBOL(dataset_nestcheck);
EXPORT_SYMBOL(get_dataset_depth);