From 47972f08f34c409a5545de10fa6e2c7262c7309e Mon Sep 17 00:00:00 2001 From: "R. Christian McDonald" Date: Tue, 16 Jan 2024 11:08:12 -0500 Subject: [PATCH] Introduce recursive '-r' option for zfs-promote(8) This patch introduces a new recurse parameter to zfs_promote. The new signature is zfs_promote(zfs_handle_t *, boolean_t). When activating boot environments, it is necessary to recursively promote datasets. External tooling like bectl(8) implement this property locally by wrapping zfs_promote. However, it is also useful to have this option '-r' in upstream zfs-promote(8). Here we push this functionality into libzfs where it can be shared by zfs-promote(8). Signed-off-by: R. Christian McDonald Sponsored by: Rubicon Communications, LLC ("Netgate") --- cmd/zfs/zfs_main.c | 34 ++++++++++++++++------------- include/libzfs.h | 2 +- lib/libzfs/libzfs_dataset.c | 43 ++++++++++++++++++++++++++++++++----- man/man8/zfs-promote.8 | 6 ++++++ 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index f67f6114d0f1..a3321ee142d7 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -311,7 +311,7 @@ get_usage(zfs_help_t idx) return (gettext("\tmount\n" "\tmount [-flvO] [-o opts] <-a | filesystem>\n")); case HELP_PROMOTE: - return (gettext("\tpromote \n")); + return (gettext("\tpromote [-r] \n")); case HELP_RECEIVE: return (gettext("\treceive [-vMnsFhu] " "[-o =] ... [-x ] ...\n" @@ -3869,32 +3869,36 @@ static int zfs_do_promote(int argc, char **argv) { zfs_handle_t *zhp; - int ret = 0; + int c, ret = 0; + boolean_t recursive = B_FALSE; - /* check options */ - if (argc > 1 && argv[1][0] == '-') { - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - argv[1][1]); - usage(B_FALSE); + while ((c = getopt(argc, argv, "r")) != -1) { + switch (c) { + case 'r': + recursive = B_TRUE; + break; + default: + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } } + argc -= optind; + argv += optind; + /* check number of arguments */ - if (argc < 2) { + if (argc < 1) { (void) fprintf(stderr, gettext("missing clone filesystem" " argument\n")); usage(B_FALSE); } - if (argc > 2) { - (void) fprintf(stderr, gettext("too many arguments\n")); - usage(B_FALSE); - } - zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); if (zhp == NULL) return (1); - ret = (zfs_promote(zhp) != 0); - + ret = (zfs_promote(zhp, recursive) != 0); zfs_close(zhp); return (ret); diff --git a/include/libzfs.h b/include/libzfs.h index 4f06b5d3c24c..c2dd195dd666 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -817,7 +817,7 @@ _LIBZFS_H int zfs_send_saved(zfs_handle_t *, sendflags_t *, int, const char *); _LIBZFS_H nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token); -_LIBZFS_H int zfs_promote(zfs_handle_t *); +_LIBZFS_H int zfs_promote(zfs_handle_t *, boolean_t); _LIBZFS_H int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t, int); _LIBZFS_H int zfs_hold_nvl(zfs_handle_t *, int, nvlist_t *); diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 727efc5a91ad..e4789b14fff4 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -4106,11 +4106,8 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) return (ret); } -/* - * Promotes the given clone fs to be the clone parent. - */ -int -zfs_promote(zfs_handle_t *zhp) +static int +zfs_promote_impl(zfs_handle_t *zhp) { libzfs_handle_t *hdl = zhp->zfs_hdl; char snapname[ZFS_MAX_DATASET_NAME_LEN]; @@ -4163,6 +4160,42 @@ zfs_promote(zfs_handle_t *zhp) return (ret); } +static int +zfs_promote_recursive_cb(zfs_handle_t *zhp, void *arg) +{ + int err, *ret = (void *)arg; + + /* Bubble up last non-zero error to caller, best we can do */ + if ((err = zfs_promote_impl(zhp)) != 0) + *ret = err; + + return (zfs_iter_filesystems(zhp, zfs_promote_recursive_cb, arg)); +} + +static int +zfs_promote_recursive_impl(zfs_handle_t *zhp) +{ + int err, ret = 0; + + /* Bubble up last non-zero error to caller, best we can do */ + if ((err = zfs_promote_recursive_cb(zhp, &ret)) != 0) + ret = err; + + return (ret); +} + +/* + * Promotes the given clone fs to be the clone parent. + */ +int +zfs_promote(zfs_handle_t *zhp, boolean_t recurse) +{ + if (recurse) + return (zfs_promote_recursive_impl(zhp)); + + return (zfs_promote_impl(zhp)); +} + typedef struct snapdata { nvlist_t *sd_nvl; const char *sd_snapname; diff --git a/man/man8/zfs-promote.8 b/man/man8/zfs-promote.8 index cdb04d05de18..b0f61185262b 100644 --- a/man/man8/zfs-promote.8 +++ b/man/man8/zfs-promote.8 @@ -39,6 +39,7 @@ .Sh SYNOPSIS .Nm zfs .Cm promote +.Op Fl r .Ar clone . .Sh DESCRIPTION @@ -55,10 +56,15 @@ The space they use moves from the origin dataset to the promoted clone, so enough space must be available to accommodate these snapshots. No new space is consumed by this operation, but the space accounting is adjusted. +.Pp The promoted clone must not have any conflicting snapshot names of its own. The .Nm zfs Cm rename subcommand can be used to rename any conflicting snapshots. +.Bl -tag -width "-H" +.It Fl r +Recursively promote all descendent datasets. +.El . .Sh EXAMPLES .\" These are, respectively, examples 10 from zfs.8