Index: lib/libbe/Makefile =================================================================== --- lib/libbe/Makefile +++ lib/libbe/Makefile @@ -1,5 +1,7 @@ # $FreeBSD$ +.include + PACKAGE= lib${LIB} LIB= be SHLIBDIR?= /lib @@ -28,4 +30,7 @@ CFLAGS+= -DNEED_SOLARIS_BOOLEAN +HAS_TESTS= YES +SUBDIR.${MK_TESTS}+= tests + .include Index: lib/libbe/be.h =================================================================== --- lib/libbe/be.h +++ lib/libbe/be.h @@ -86,6 +86,7 @@ int be_create_from_existing(libbe_handle_t *, const char *, const char *); int be_create_from_existing_snap(libbe_handle_t *, const char *, const char *); int be_snapshot(libbe_handle_t *, const char *, const char *, bool, char *); +int _be_create(libbe_handle_t *, const char *, const char *, bool); /* Bootenv manipulation functions */ int be_rename(libbe_handle_t *, const char *, const char *); Index: lib/libbe/be.c =================================================================== --- lib/libbe/be.c +++ lib/libbe/be.c @@ -365,7 +365,7 @@ char *dsname; zfs_handle_t *snap_hdl; nvlist_t *props; - struct libbe_deep_clone *isdc, sdc; + struct libbe_deep_clone *isdc; struct libbe_dccb dccb; isdc = (struct libbe_deep_clone *)data; @@ -407,53 +407,34 @@ nvlist_free(props); zfs_close(snap_hdl); - /* Failed to clone */ - if (err != BE_ERR_SUCCESS) - return (set_error(isdc->lbh, err)); - - sdc.lbh = isdc->lbh; - sdc.bename = NULL; - sdc.snapname = isdc->snapname; - sdc.be_root = (char *)&be_path; - - err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); - - return (err); + return (set_error(isdc->lbh, err)); } -/* - * Create the boot environment from pre-existing snapshot - */ +/* create a boot environment with a given name from a given snapshot */ int -be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, - const char *snap) +_be_create(libbe_handle_t *lbh, const char *bename, const char *snap, bool recursive) { int err; - char be_path[BE_MAXPATHLEN]; char snap_path[BE_MAXPATHLEN]; - const char *bename; char *parentname, *snapname; zfs_handle_t *parent_hdl; - struct libbe_deep_clone sdc; + struct libbe_deep_clone ldc; - if ((err = be_validate_name(lbh, name)) != 0) + /* check validity of bootenv name */ + if ((err = be_validate_name(lbh, bename)) != 0) return (set_error(lbh, err)); + + /* construct and validate the snapshot path */ if ((err = be_root_concat(lbh, snap, snap_path)) != 0) return (set_error(lbh, err)); if ((err = be_validate_snap(lbh, snap_path)) != 0) return (set_error(lbh, err)); - if ((err = be_root_concat(lbh, name, be_path)) != 0) - return (set_error(lbh, err)); - - if ((bename = strrchr(name, '/')) == NULL) - bename = name; - else - bename++; - + /* copy dataset path */ if ((parentname = strdup(snap_path)) == NULL) return (set_error(lbh, BE_ERR_UNKNOWN)); + /* split dataset name from snapshot name */ snapname = strchr(parentname, '@'); if (snapname == NULL) { free(parentname); @@ -462,18 +443,42 @@ *snapname = '\0'; snapname++; - sdc.lbh = lbh; - sdc.bename = bename; - sdc.snapname = snapname; - sdc.be_root = lbh->root; + /* set-up deep clone struct */ + ldc.lbh = lbh; + ldc.bename = bename; + ldc.snapname = snapname; + ldc.be_root = lbh->root; + /* open the dataset */ parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); - err = be_deep_clone(parent_hdl, &sdc); + + /* clone it */ + if ((err = be_deep_clone(parent_hdl, &ldc) != 0)) + return err; + + if (recursive) { + char be_path[BE_MAXPATHLEN]; + snprintf(be_path, sizeof(be_path), "%s/%s", ldc.be_root, ldc.bename); + ldc.bename = NULL; + ldc.be_root = be_path; + err = zfs_iter_filesystems(parent_hdl, be_deep_clone, &ldc); + } free(parentname); return (set_error(lbh, err)); } +/* + * Create the boot environment from pre-existing snapshot + */ +int +be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename, + const char *snap) +{ + int err = _be_create(lbh, bename, snap, true); + return (set_error(lbh, err)); +} + /* * Create a boot environment from an existing boot environment @@ -487,7 +492,7 @@ if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0) return (set_error(lbh, err)); - err = be_create_from_existing_snap(lbh, name, (char *)buf); + err = _be_create(lbh, name, buf, true); return (set_error(lbh, err)); } Index: lib/libbe/tests/Makefile =================================================================== --- /dev/null +++ lib/libbe/tests/Makefile @@ -0,0 +1,18 @@ +PACKAGE= tests + +ATF_TESTS_SH+= be_create + +PROGS= target_prog +SRCS_target_prog= target_prog.c +BINDIR_target_prog= ${TESTSDIR} + +LIBADD+= zfs +LIBADD+= nvpair +LIBADD+= be + +CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris +CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common + +CFLAGS+= -DNEED_SOLARIS_BOOLEAN + +.include Index: lib/libbe/tests/be_create.sh =================================================================== --- /dev/null +++ lib/libbe/tests/be_create.sh @@ -0,0 +1,90 @@ +# The code for the following tests was copied from the +# bectl tests found in src/sbin/bectl/tests, modified as needed. + +# Establishes a libbe zpool that can be used for some light testing; contains +# a 'default' BE and not much else. +libbe_create_setup() +{ + zpool=$1 + disk=$2 + mnt=$3 + + kldload -n -q zfs || atf_skip "ZFS module not loaded on the current system" + atf_check mkdir -p ${mnt} + atf_check truncate -s 1G ${disk} + atf_check zpool create -o altroot=${mnt} ${zpool} ${disk} + atf_check zfs create -o mountpoint=none ${zpool}/ROOT + atf_check zfs create -o mountpoint=/ -o canmount=noauto \ + ${zpool}/ROOT/default + atf_check zfs create -o mountpoint=/boot -o canmount=noauto \ + ${zpool}/ROOT/default/boot + +} + +libbe_cleanup() +{ + zpool=$1 + + if zpool get health ${zpool} >/dev/null 2>&1; then + zpool destroy ${zpool} + fi +} + +atf_test_case libbe_create cleanup +libbe_create_head() +{ + atf_set "descr" "check _be_create from libbe" + atf_set "require.user" root +} +libbe_create_body() +{ + cwd=$(atf_get_srcdir) + zpool=libbe_test + disk=${cwd}/disk.img + mount=${cwd}/mnt + prog=${cwd}/./target_prog + + # preliminary setup/checks + atf_require_prog $prog + libbe_create_setup ${zpool} ${disk} ${mount} + + # a recursive and non-recursive snapshot to test against + atf_check zfs snapshot ${zpool}/ROOT/default@non-recursive + atf_check zfs snapshot -r ${zpool}/ROOT/default@recursive + + # create a boot environment named 'nonrecursive' + # from the provided snapshot. + atf_check -o empty -s exit:0 $prog "${zpool}/ROOT" \ + nonrecursive \ + "${zpool}/ROOT/default@non-recursive" \ + false + # the nonrecursive dataset should exist + atf_check -o not-empty -s exit:0 \ + zfs list "${zpool}/ROOT/nonrecursive" + # boot, a child dataset of nonrecursive should not exist. + atf_check -e not-empty -s not-exit:0 \ + zfs list "${zpool}/ROOT/nonrecursive/boot" + + # create a boot environment recursively with the + # name 'recursive'. + atf_check -o empty -s exit:0 $prog "${zpool}/ROOT" \ + recursive \ + "${zpool}/ROOT/default@recursive" \ + true + # the recursive dataset should exist + atf_check -o not-empty -s exit:0 \ + zfs list "${zpool}/ROOT/recursive" + # the child dataset should exist + atf_check -o not-empty -s exit:0 \ + zfs list "${zpool}/ROOT/recursive/boot" +} + +libbe_create_cleanup() +{ + libbe_cleanup libbe_test +} + +atf_init_test_cases() +{ + atf_add_test_case libbe_create +} Index: lib/libbe/tests/target_prog.c =================================================================== --- /dev/null +++ lib/libbe/tests/target_prog.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#include + +/* + * argv[1] = root boot environment (e.g. zroot/ROOT), + * argv[2] = name of boot environment to create + * argv[3] = snapshot to create boot environment from + * argv[4] = create boot environment recursively (true/false) + */ +int main(int argc, char *argv[]) { + + libbe_handle_t *lbh; + + if (argc != 5) + return -1; + + if ((lbh = libbe_init(argv[1])) == NULL) + return -1; + + libbe_print_on_error(lbh, true); + + return (_be_create(lbh, argv[2], + argv[3], + strcmp(argv[4], "true") == 0 ? true : false)); +}