From owner-svn-src-all@freebsd.org Tue Sep 3 14:08:04 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id CAA75DD63C; Tue, 3 Sep 2019 14:07:11 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2610:1c1:1:6074::16:84]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46N80H4jMDz4QLQ; Tue, 3 Sep 2019 14:07:11 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 1452) id 7791B1B236; Tue, 3 Sep 2019 14:06:31 +0000 (UTC) X-Original-To: yuripv@localmail.freebsd.org Delivered-To: yuripv@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [96.47.72.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id A58B76DA3; Sat, 20 Apr 2019 04:16:57 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [96.47.72.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 5BF1581F96; Sat, 20 Apr 2019 04:16:56 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 538) id 1472C6D45; Sat, 20 Apr 2019 04:16:56 +0000 (UTC) Delivered-To: src-committers@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [96.47.72.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id 80F006D2C for ; Sat, 20 Apr 2019 04:16:53 +0000 (UTC) (envelope-from kevans@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 54D0B81F7F; Sat, 20 Apr 2019 04:16:53 +0000 (UTC) (envelope-from kevans@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 2EC888CD3; Sat, 20 Apr 2019 04:16:53 +0000 (UTC) (envelope-from kevans@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x3K4GrsL077699; Sat, 20 Apr 2019 04:16:53 GMT (envelope-from kevans@FreeBSD.org) Received: (from kevans@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x3K4GpdU077690; Sat, 20 Apr 2019 04:16:51 GMT (envelope-from kevans@FreeBSD.org) Message-Id: <201904200416.x3K4GpdU077690@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: kevans set sender to kevans@FreeBSD.org using -f From: Kyle Evans To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r346429 - in stable/11: . contrib/mdocml etc/mtree lib lib/libbe rescue/rescue sbin sbin/bectl sbin/bectl/tests share/mk tools/build/mk X-SVN-Group: stable-11 X-SVN-Commit-Author: kevans X-SVN-Commit-Paths: in stable/11: . contrib/mdocml etc/mtree lib lib/libbe rescue/rescue sbin sbin/bectl sbin/bectl/tests share/mk tools/build/mk X-SVN-Commit-Revision: 346429 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk X-Loop: FreeBSD.org Sender: owner-src-committers@freebsd.org X-Rspamd-Queue-Id: 5BF1581F96 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[freebsd.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_SHORT(-0.97)[-0.970,0]; ASN(0.00)[asn:11403, ipnet:96.47.64.0/20, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] Status: O X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Date: Tue, 03 Sep 2019 14:08:04 -0000 X-Original-Date: Sat, 20 Apr 2019 04:16:51 +0000 (UTC) X-List-Received-Date: Tue, 03 Sep 2019 14:08:04 -0000 Author: kevans Date: Sat Apr 20 04:16:51 2019 New Revision: 346429 URL: https://svnweb.freebsd.org/changeset/base/346429 Log: MFC bectl(8)/libbe(3): r337663-337664,337667,337697-337699,337800,337805, 337915-337918,337921,337924,337947,337993-337995,338221-338222,338303, 338417,339047,339972,339994,340334,340507-340508,340592-340594, 340635-340636,340722-340723,340974,342466,342849,342903,342911,343335, 343543,343977,343993-343994,344034,344067,344084,345302,345769, 345845-345846,345848,346082 There are simply too many small changes to enumerate; in summary: bectl(8)/libbe(3) has been introduced from current state in -CURRENT and added to the stable/11 rescue build. bectl(8) is a tool for managing ZFS boot environments, largely inspired by beadm. It includes features such as being able to jail a boot environment or easily mount it for modification. Relnotes: probably Added: stable/11/lib/libbe/ - copied from r337663, head/lib/libbe/ stable/11/lib/libbe/Makefile - copied, changed from r337995, head/lib/libbe/Makefile stable/11/sbin/bectl/ - copied from r337663, head/sbin/bectl/ stable/11/sbin/bectl/tests/ - copied from r340594, head/sbin/bectl/tests/ Modified: stable/11/Makefile.inc1 stable/11/contrib/mdocml/lib.in stable/11/etc/mtree/BSD.tests.dist stable/11/lib/Makefile stable/11/lib/libbe/be.c stable/11/lib/libbe/be.h stable/11/lib/libbe/be_access.c stable/11/lib/libbe/be_error.c stable/11/lib/libbe/be_impl.h stable/11/lib/libbe/be_info.c stable/11/lib/libbe/libbe.3 stable/11/rescue/rescue/Makefile stable/11/sbin/Makefile stable/11/sbin/bectl/Makefile stable/11/sbin/bectl/bectl.8 stable/11/sbin/bectl/bectl.c stable/11/sbin/bectl/bectl_jail.c stable/11/sbin/bectl/bectl_list.c stable/11/sbin/bectl/tests/bectl_test.sh stable/11/share/mk/bsd.libnames.mk stable/11/share/mk/src.libnames.mk stable/11/tools/build/mk/OptionalObsoleteFiles.inc Directory Properties: stable/11/ (props changed) Modified: stable/11/Makefile.inc1 ============================================================================== --- stable/11/Makefile.inc1 Sat Apr 20 03:21:47 2019 (r346428) +++ stable/11/Makefile.inc1 Sat Apr 20 04:16:51 2019 (r346429) @@ -2155,7 +2155,7 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \ ${_cddl_lib_libuutil} \ ${_cddl_lib_libavl} \ - ${_cddl_lib_libzfs_core} \ + ${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \ ${_cddl_lib_libctf} \ lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \ ${_secure_lib_libcrypto} ${_lib_libldns} \ @@ -2224,7 +2224,15 @@ _cddl_lib_libavl= cddl/lib/libavl _cddl_lib_libuutil= cddl/lib/libuutil .if ${MK_ZFS} != "no" _cddl_lib_libzfs_core= cddl/lib/libzfs_core +_cddl_lib_libzfs= cddl/lib/libzfs + cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L + +cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L +cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L +cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L + +lib/libbe__L: cddl/lib/libzfs__L .endif _cddl_lib_libctf= cddl/lib/libctf _cddl_lib= cddl/lib Modified: stable/11/contrib/mdocml/lib.in ============================================================================== --- stable/11/contrib/mdocml/lib.in Sat Apr 20 03:21:47 2019 (r346428) +++ stable/11/contrib/mdocml/lib.in Sat Apr 20 04:16:51 2019 (r346429) @@ -28,6 +28,7 @@ LINE("lib80211", "802.11 Wireless Network Management L LINE("libarchive", "Streaming Archive Library (libarchive, \\-larchive)") LINE("libarm", "ARM Architecture Library (libarm, \\-larm)") LINE("libarm32", "ARM32 Architecture Library (libarm32, \\-larm32)") +LINE("libbe", "Boot Environment Library (libbe, \\-lbe)") LINE("libbluetooth", "Bluetooth Library (libbluetooth, \\-lbluetooth)") LINE("libbsm", "Basic Security Module Library (libbsm, \\-lbsm)") LINE("libc", "Standard C\\~Library (libc, \\-lc)") Modified: stable/11/etc/mtree/BSD.tests.dist ============================================================================== --- stable/11/etc/mtree/BSD.tests.dist Sat Apr 20 03:21:47 2019 (r346428) +++ stable/11/etc/mtree/BSD.tests.dist Sat Apr 20 04:16:51 2019 (r346429) @@ -380,6 +380,8 @@ .. .. sbin + bectl + .. dhclient .. devd Modified: stable/11/lib/Makefile ============================================================================== --- stable/11/lib/Makefile Sat Apr 20 03:21:47 2019 (r346428) +++ stable/11/lib/Makefile Sat Apr 20 04:16:51 2019 (r346429) @@ -290,6 +290,7 @@ _libproc= libproc _librtld_db= librtld_db .endif SUBDIR.${MK_OFED}+= ofed +SUBDIR.${MK_ZFS}+= libbe SUBDIR.${MK_OPENMP}+= libomp Copied and modified: stable/11/lib/libbe/Makefile (from r337995, head/lib/libbe/Makefile) ============================================================================== --- head/lib/libbe/Makefile Sat Aug 18 03:20:59 2018 (r337995, copy source) +++ stable/11/lib/libbe/Makefile Sat Apr 20 04:16:51 2019 (r346429) @@ -2,6 +2,7 @@ PACKAGE= lib${LIB} LIB= be +SHLIBDIR?= /lib SHLIB_MAJOR= 1 SHLIB_MINOR= 0 Modified: stable/11/lib/libbe/be.c ============================================================================== --- head/lib/libbe/be.c Sat Aug 11 23:50:09 2018 (r337663) +++ stable/11/lib/libbe/be.c Sat Apr 20 04:16:51 2019 (r346429) @@ -29,11 +29,12 @@ #include __FBSDID("$FreeBSD$"); +#include +#include #include -#include +#include #include -#include #include #include #include @@ -44,31 +45,49 @@ __FBSDID("$FreeBSD$"); #include "be.h" #include "be_impl.h" +struct be_destroy_data { + libbe_handle_t *lbh; + char *snapname; +}; + #if SOON static int be_create_child_noent(libbe_handle_t *lbh, const char *active, const char *child_path); static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); #endif +/* Arbitrary... should tune */ +#define BE_SNAP_SERIAL_MAX 1024 + /* * Iterator function for locating the rootfs amongst the children of the * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. */ static int -be_locate_rootfs(zfs_handle_t *chkds, void *data) +be_locate_rootfs(libbe_handle_t *lbh) { - libbe_handle_t *lbh; - char *mntpoint; + struct statfs sfs; + struct extmnttab entry; + zfs_handle_t *zfs; - lbh = (libbe_handle_t *)data; - if (lbh == NULL) + /* + * Check first if root is ZFS; if not, we'll bail on rootfs capture. + * Unfortunately needed because zfs_path_to_zhandle will emit to + * stderr if / isn't actually a ZFS filesystem, which we'd like + * to avoid. + */ + if (statfs("/", &sfs) == 0) { + statfs2mnttab(&sfs, &entry); + if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) + return (1); + } else return (1); - - if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) { - strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN); + zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); + if (zfs == NULL) return (1); - } + strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); + zfs_close(zfs); return (0); } @@ -77,52 +96,41 @@ be_locate_rootfs(zfs_handle_t *chkds, void *data) * dataset, for example, zroot/ROOT. */ libbe_handle_t * -libbe_init(void) +libbe_init(const char *root) { - struct stat sb; - dev_t root_dev, boot_dev; + char altroot[MAXPATHLEN]; libbe_handle_t *lbh; - zfs_handle_t *rootds; char *poolname, *pos; int pnamelen; lbh = NULL; poolname = pos = NULL; - pnamelen = 0; - rootds = NULL; - /* Verify that /boot and / are mounted on the same filesystem */ - /* TODO: use errno here?? */ - if (stat("/", &sb) != 0) - goto err; - - root_dev = sb.st_dev; - - if (stat("/boot", &sb) != 0) - goto err; - - boot_dev = sb.st_dev; - - if (root_dev != boot_dev) { - fprintf(stderr, "/ and /boot not on same device, quitting\n"); - goto err; - } - if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) goto err; if ((lbh->lzh = libzfs_init()) == NULL) goto err; - /* Obtain path to boot environment root */ - if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) - goto err; + /* + * Grab rootfs, we'll work backwards from there if an optional BE root + * has not been passed in. + */ + if (be_locate_rootfs(lbh) != 0) { + if (root == NULL) + goto err; + *lbh->rootfs = '\0'; + } + if (root == NULL) { + /* Strip off the final slash from rootfs to get the be root */ + strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); + pos = strrchr(lbh->root, '/'); + if (pos == NULL) + goto err; + *pos = '\0'; + } else + strlcpy(lbh->root, root, sizeof(lbh->root)); - /* Remove leading 'zfs:' if present, otherwise use value as-is */ - if (strcmp(lbh->root, "zfs:") == 0) - strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char), - BE_MAXPATHLEN); - if ((pos = strchr(lbh->root, '/')) == NULL) goto err; @@ -131,26 +139,21 @@ libbe_init(void) if (poolname == NULL) goto err; - strncpy(poolname, lbh->root, pnamelen); - poolname[pnamelen] = '\0'; + strlcpy(poolname, lbh->root, pnamelen + 1); if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) goto err; + free(poolname); + poolname = NULL; if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, - BE_MAXPATHLEN, NULL, true) != 0) + sizeof(lbh->bootfs), NULL, true) != 0) goto err; - /* Obtain path to boot environment rootfs (currently booted) */ - /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ - if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL) - goto err; + if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT, + altroot, sizeof(altroot), NULL, true) == 0 && + strcmp(altroot, "-") != 0) + lbh->altroot_len = strlen(altroot); - zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); - zfs_close(rootds); - rootds = NULL; - if (*lbh->rootfs == '\0') - goto err; - return (lbh); err: if (lbh != NULL) { @@ -160,8 +163,6 @@ err: libzfs_fini(lbh->lzh); free(lbh); } - if (rootds != NULL) - zfs_close(rootds); free(poolname); return (NULL); } @@ -193,12 +194,38 @@ be_nicenum(uint64_t num, char *buf, size_t buflen) static int be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) { + char path[BE_MAXPATHLEN]; + struct be_destroy_data *bdd; + zfs_handle_t *snap; int err; - if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0) + bdd = (struct be_destroy_data *)data; + if (bdd->snapname == NULL) { + err = zfs_iter_children(zfs_hdl, be_destroy_cb, data); + if (err != 0) + return (err); + return (zfs_destroy(zfs_hdl, false)); + } + /* If we're dealing with snapshots instead, delete that one alone */ + err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data); + if (err != 0) return (err); - if ((err = zfs_destroy(zfs_hdl, false)) != 0) - return (err); + /* + * This part is intentionally glossing over any potential errors, + * because there's a lot less potential for errors when we're cleaning + * up snapshots rather than a full deep BE. The primary error case + * here being if the snapshot doesn't exist in the first place, which + * the caller will likely deem insignificant as long as it doesn't + * exist after the call. Thus, such a missing snapshot shouldn't jam + * up the destruction. + */ + snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl), + bdd->snapname); + if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) + return (0); + snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT); + if (snap != NULL) + zfs_destroy(snap, false); return (0); } @@ -206,85 +233,144 @@ be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) * Destroy the boot environment or snapshot specified by the name * parameter. Options are or'd together with the possible values: * BE_DESTROY_FORCE : forces operation on mounted datasets + * BE_DESTROY_ORIGIN: destroy the origin snapshot as well */ int be_destroy(libbe_handle_t *lbh, const char *name, int options) { + struct be_destroy_data bdd; + char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; zfs_handle_t *fs; - char path[BE_MAXPATHLEN]; - char *p; + char *snapdelim; int err, force, mounted; + size_t rootlen; - p = path; + bdd.lbh = lbh; + bdd.snapname = NULL; force = options & BE_DESTROY_FORCE; - err = BE_ERR_SUCCESS; + *origin = '\0'; be_root_concat(lbh, name, path); - if (strchr(name, '@') == NULL) { + if ((snapdelim = strchr(path, '@')) == NULL) { if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) return (set_error(lbh, BE_ERR_NOENT)); - if (strcmp(path, lbh->rootfs) == 0) + if (strcmp(path, lbh->rootfs) == 0 || + strcmp(path, lbh->bootfs) == 0) return (set_error(lbh, BE_ERR_DESTROYACT)); - fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); - } else { + fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); + if (fs == NULL) + return (set_error(lbh, BE_ERR_ZFSOPEN)); + if ((options & BE_DESTROY_ORIGIN) != 0 && + zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), + NULL, NULL, 0, 1) != 0) + return (set_error(lbh, BE_ERR_NOORIGIN)); + + /* Don't destroy a mounted dataset unless force is specified */ + if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { + if (force) { + zfs_unmount(fs, NULL, 0); + } else { + free(bdd.snapname); + return (set_error(lbh, BE_ERR_DESTROYMNT)); + } + } + } else { if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) return (set_error(lbh, BE_ERR_NOENT)); - fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); + bdd.snapname = strdup(snapdelim + 1); + if (bdd.snapname == NULL) + return (set_error(lbh, BE_ERR_NOMEM)); + *snapdelim = '\0'; + fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); + if (fs == NULL) { + free(bdd.snapname); + return (set_error(lbh, BE_ERR_ZFSOPEN)); + } } - if (fs == NULL) - return (set_error(lbh, BE_ERR_ZFSOPEN)); - - /* Check if mounted, unmount if force is specified */ - if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { - if (force) - zfs_unmount(fs, NULL, 0); - else - return (set_error(lbh, BE_ERR_DESTROYMNT)); - } - - if ((err = be_destroy_cb(fs, NULL)) != 0) { + err = be_destroy_cb(fs, &bdd); + zfs_close(fs); + free(bdd.snapname); + if (err != 0) { /* Children are still present or the mount is referenced */ if (err == EBUSY) return (set_error(lbh, BE_ERR_DESTROYMNT)); return (set_error(lbh, BE_ERR_UNKNOWN)); } - return (0); + if ((options & BE_DESTROY_ORIGIN) == 0) + return (0); + + /* The origin can't possibly be shorter than the BE root */ + rootlen = strlen(lbh->root); + if (*origin == '\0' || strlen(origin) <= rootlen + 1) + return (set_error(lbh, BE_ERR_INVORIGIN)); + + /* + * We'll be chopping off the BE root and running this back through + * be_destroy, so that we properly handle the origin snapshot whether + * it be that of a deep BE or not. + */ + if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') + return (0); + + return (be_destroy(lbh, origin + rootlen + 1, + options & ~BE_DESTROY_ORIGIN)); } +static void +be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) +{ + time_t rawtime; + int len, serial; + time(&rawtime); + len = strlen(buf); + len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime)); + /* No room for serial... caller will do its best */ + if (buflen - len < 2) + return; + + for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) { + snprintf(buf + len, buflen - len, "-%d", serial); + if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) + return; + } +} + int be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, bool recursive, char *result) { char buf[BE_MAXPATHLEN]; - time_t rawtime; - int len, err; + int err; be_root_concat(lbh, source, buf); - if (!be_exists(lbh, buf)) - return (BE_ERR_NOENT); + if ((err = be_exists(lbh, buf)) != 0) + return (set_error(lbh, err)); if (snap_name != NULL) { - strcat(buf, "@"); - strcat(buf, snap_name); + if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) + return (set_error(lbh, BE_ERR_INVALIDNAME)); + + if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) + return (set_error(lbh, BE_ERR_INVALIDNAME)); + if (result != NULL) snprintf(result, BE_MAXPATHLEN, "%s@%s", source, snap_name); } else { - time(&rawtime); - len = strlen(buf); - strftime(buf + len, BE_MAXPATHLEN - len, - "@%F-%T", localtime(&rawtime)); - if (result != NULL) - strcpy(result, strrchr(buf, '/') + 1); + be_setup_snapshot_name(lbh, buf, sizeof(buf)); + + if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, + sizeof(buf)) >= sizeof(buf)) + return (set_error(lbh, BE_ERR_INVALIDNAME)); } if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { @@ -322,7 +408,6 @@ be_create(libbe_handle_t *lbh, const char *name) return (set_error(lbh, err)); } - static int be_deep_clone_prop(int prop, void *cb) { @@ -331,6 +416,7 @@ be_deep_clone_prop(int prop, void *cb) zprop_source_t src; char pval[BE_MAXPATHLEN]; char source[BE_MAXPATHLEN]; + char *val; dccb = cb; /* Skip some properties we don't want to touch */ @@ -350,8 +436,13 @@ be_deep_clone_prop(int prop, void *cb) if (src != ZPROP_SRC_LOCAL) return (ZPROP_CONT); - nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); + /* Augment mountpoint with altroot, if needed */ + val = pval; + if (prop == ZFS_PROP_MOUNTPOINT) + val = be_mountpoint_augmented(dccb->lbh, val); + nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); + return (ZPROP_CONT); } @@ -391,26 +482,23 @@ be_deep_clone(zfs_handle_t *ds, void *data) nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); + dccb.lbh = isdc->lbh; dccb.zhp = ds; dccb.props = props; if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) return (-1); - if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) { - switch (err) { - case EZFS_SUCCESS: - err = BE_ERR_SUCCESS; - break; - default: - err = BE_ERR_ZFSCLONE; - break; - } - } + if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) + err = BE_ERR_ZFSCLONE; 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; @@ -451,14 +539,13 @@ be_create_from_existing_snap(libbe_handle_t *lbh, cons else bename++; - if ((parentname = strdup(snap_path)) == NULL) { - err = BE_ERR_UNKNOWN; - return (set_error(lbh, err)); - } + if ((parentname = strdup(snap_path)) == NULL) + return (set_error(lbh, BE_ERR_UNKNOWN)); + snapname = strchr(parentname, '@'); if (snapname == NULL) { - err = BE_ERR_UNKNOWN; - return (set_error(lbh, err)); + free(parentname); + return (set_error(lbh, BE_ERR_UNKNOWN)); } *snapname = '\0'; snapname++; @@ -471,6 +558,7 @@ be_create_from_existing_snap(libbe_handle_t *lbh, cons parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); err = be_deep_clone(parent_hdl, &sdc); + free(parentname); return (set_error(lbh, err)); } @@ -484,7 +572,7 @@ be_create_from_existing(libbe_handle_t *lbh, const cha int err; char buf[BE_MAXPATHLEN]; - if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) + 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); @@ -501,39 +589,18 @@ be_create_from_existing(libbe_handle_t *lbh, const cha int be_validate_snap(libbe_handle_t *lbh, const char *snap_name) { - zfs_handle_t *zfs_hdl; - char buf[BE_MAXPATHLEN]; - char *delim_pos; - int err = BE_ERR_SUCCESS; if (strlen(snap_name) >= BE_MAXPATHLEN) return (BE_ERR_PATHLEN); + if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT)) + return (BE_ERR_INVALIDNAME); + if (!zfs_dataset_exists(lbh->lzh, snap_name, ZFS_TYPE_SNAPSHOT)) return (BE_ERR_NOENT); - strncpy(buf, snap_name, BE_MAXPATHLEN); - - /* Find the base filesystem of the snapshot */ - if ((delim_pos = strchr(buf, '@')) == NULL) - return (BE_ERR_INVALIDNAME); - *delim_pos = '\0'; - - if ((zfs_hdl = - zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) - return (BE_ERR_NOORIGIN); - - if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN, - NULL, NULL, 0, 1)) != 0) - err = BE_ERR_INVORIGIN; - - if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) - err = BE_ERR_INVORIGIN; - - zfs_close(zfs_hdl); - - return (err); + return (BE_ERR_SUCCESS); } @@ -561,7 +628,7 @@ be_root_concat(libbe_handle_t *lbh, const char *name, if (name_len >= BE_MAXPATHLEN) return (BE_ERR_PATHLEN); - strncpy(result, name, BE_MAXPATHLEN); + strlcpy(result, name, BE_MAXPATHLEN); return (BE_ERR_SUCCESS); } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, @@ -575,18 +642,23 @@ be_root_concat(libbe_handle_t *lbh, const char *name, /* * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns - * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME. + * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME + * or BE_ERR_PATHLEN. * Does not set internal library error state. */ int -be_validate_name(libbe_handle_t *lbh __unused, const char *name) +be_validate_name(libbe_handle_t *lbh, const char *name) { - for (int i = 0; *name; i++) { - char c = *(name++); - if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) - continue; + + /* + * Impose the additional restriction that the entire dataset name must + * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. + */ + if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) + return (BE_ERR_PATHLEN); + + if (!zfs_name_valid(name, ZFS_TYPE_DATASET)) return (BE_ERR_INVALIDNAME); - } return (BE_ERR_SUCCESS); } @@ -603,18 +675,17 @@ be_rename(libbe_handle_t *lbh, const char *old, const zfs_handle_t *zfs_hdl; int err; + /* + * be_validate_name is documented not to set error state, so we should + * do so here. + */ + if ((err = be_validate_name(lbh, new)) != 0) + return (set_error(lbh, err)); if ((err = be_root_concat(lbh, old, full_old)) != 0) return (set_error(lbh, err)); if ((err = be_root_concat(lbh, new, full_new)) != 0) return (set_error(lbh, err)); - if ((err = be_validate_name(lbh, new)) != 0) - return (err); - - /* Check if old is active BE */ - if (strcmp(full_old, be_active_path(lbh)) == 0) - return (set_error(lbh, BE_ERR_MOUNTED)); - if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) return (set_error(lbh, BE_ERR_NOENT)); @@ -625,20 +696,17 @@ be_rename(libbe_handle_t *lbh, const char *old, const ZFS_TYPE_FILESYSTEM)) == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); - /* XXX TODO: Allow a force flag */ - if (zfs_is_mounted(zfs_hdl, NULL)) { - zfs_close(zfs_hdl); - return (set_error(lbh, BE_ERR_MOUNTED)); - } - /* recurse, nounmount, forceunmount */ - struct renameflags flags = { 0, 0, 0 }; + struct renameflags flags = { + .nounmount = 1, + }; err = zfs_rename(zfs_hdl, NULL, full_new, flags); zfs_close(zfs_hdl); - - return (set_error(lbh, err)); + if (err != 0) + return (set_error(lbh, BE_ERR_UNKNOWN)); + return (0); } @@ -670,33 +738,14 @@ int be_import(libbe_handle_t *lbh, const char *bootenv, int fd) { char buf[BE_MAXPATHLEN]; - time_t rawtime; nvlist_t *props; zfs_handle_t *zfs; - int err, len; - char nbuf[24]; + recvflags_t flags = { .nomount = 1 }; + int err; - /* - * We don't need this to be incredibly random, just unique enough that - * it won't conflict with an existing dataset name. Chopping time - * down to 32 bits is probably good enough for this. - */ - snprintf(nbuf, 24, "tmp%u", - (uint32_t)(time(NULL) & 0xFFFFFFFF)); - if ((err = be_root_concat(lbh, nbuf, buf)) != 0) - /* - * Technically this is our problem, but we try to use short - * enough names that we won't run into problems except in - * worst-case BE root approaching MAXPATHLEN. - */ - return (set_error(lbh, BE_ERR_PATHLEN)); + be_root_concat(lbh, bootenv, buf); - time(&rawtime); - len = strlen(buf); - strftime(buf + len, BE_MAXPATHLEN - len, - "@%F-%T", localtime(&rawtime)); - - if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { + if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) { switch (err) { case EINVAL: return (set_error(lbh, BE_ERR_NOORIGIN)); @@ -709,22 +758,22 @@ be_import(libbe_handle_t *lbh, const char *bootenv, in } } - if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) + if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL) return (set_error(lbh, BE_ERR_ZFSOPEN)); nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); nvlist_add_string(props, "mountpoint", "/"); - be_root_concat(lbh, bootenv, buf); + err = zfs_prop_set_list(zfs, props); + nvlist_free(props); - err = zfs_clone(zfs, buf, props); zfs_close(zfs); - nvlist_free(props); + if (err != 0) + return (set_error(lbh, BE_ERR_UNKNOWN)); - /* XXX TODO: Figure out how to destroy the ghost... */ - return (BE_ERR_SUCCESS); + return (0); } #if SOON @@ -901,21 +950,38 @@ be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, return (0); } +/* + * Deactivate old BE dataset; currently just sets canmount=noauto + */ +static int +be_deactivate(libbe_handle_t *lbh, const char *ds) +{ + zfs_handle_t *zfs; + if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) + return (1); + if (zfs_prop_set(zfs, "canmount", "noauto") != 0) + return (1); + zfs_close(zfs); + return (0); +} + int be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) { char be_path[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; + nvlist_t *config, *dsprops, *vdevs; + char *origin; uint64_t pool_guid; - nvlist_t *config, *vdevs; + zfs_handle_t *zhp; int err; be_root_concat(lbh, bootenv, be_path); /* Note: be_exists fails if mountpoint is not / */ - if (!be_exists(lbh, be_path)) - return (BE_ERR_NOENT); + if ((err = be_exists(lbh, be_path)) != 0) + return (set_error(lbh, err)); if (temporary) { config = zpool_get_config(lbh->active_phandle, NULL); @@ -929,9 +995,7 @@ be_activate(libbe_handle_t *lbh, const char *bootenv, return (set_error(lbh, BE_ERR_UNKNOWN)); /* Expected format according to zfsbootcfg(8) man */ - strcpy(buf, "zfs:"); - strcat(buf, be_path); - strcat(buf, ":"); + snprintf(buf, sizeof(buf), "zfs:%s:", be_path); /* We have no config tree */ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, @@ -940,16 +1004,35 @@ be_activate(libbe_handle_t *lbh, const char *bootenv, return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); } else { + if (be_deactivate(lbh, lbh->bootfs) != 0) + return (-1); + /* Obtain bootenv zpool */ err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); + if (err) + return (-1); - switch (err) { - case 0: - return (BE_ERR_SUCCESS); + zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); + if (zhp == NULL) + return (-1); - default: - /* XXX TODO correct errors */ + if (be_prop_list_alloc(&dsprops) != 0) return (-1); + + if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { + nvlist_free(dsprops); + return (-1); } + + if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) + err = zfs_promote(zhp); + nvlist_free(dsprops); + + zfs_close(zhp); + + if (err) + return (-1); } + + return (BE_ERR_SUCCESS); } Modified: stable/11/lib/libbe/be.h ============================================================================== --- head/lib/libbe/be.h Sat Aug 11 23:50:09 2018 (r337663) +++ stable/11/lib/libbe/be.h Sat Apr 20 04:16:51 2019 (r346429) @@ -49,7 +49,7 @@ typedef enum be_error { BE_ERR_BADPATH, /* path not suitable for operation */ BE_ERR_PATHBUSY, /* requested path is busy */ BE_ERR_PATHLEN, /* provided name exceeds maximum length limit */ - BE_ERR_INVORIGIN, /* snapshot origin's mountpoint is not '/' */ + BE_ERR_BADMOUNT, /* mountpoint is not '/' */ BE_ERR_NOORIGIN, /* could not open snapshot's origin */ BE_ERR_MOUNTED, /* boot environment is already mounted */ BE_ERR_NOMOUNT, /* boot environment is not mounted */ @@ -59,11 +59,12 @@ typedef enum be_error { BE_ERR_NOPOOL, /* operation not supported on this pool */ BE_ERR_NOMEM, /* insufficient memory */ BE_ERR_UNKNOWN, /* unknown error */ + BE_ERR_INVORIGIN, /* invalid origin */ } be_error_t; /* Library handling functions: be.c */ -libbe_handle_t *libbe_init(void); +libbe_handle_t *libbe_init(const char *root); void libbe_close(libbe_handle_t *); /* Bootenv information functions: be_info.c */ @@ -93,7 +94,8 @@ int be_rename(libbe_handle_t *, const char *, const ch /* Bootenv removal functions */ typedef enum { - BE_DESTROY_FORCE = 1 << 0, + BE_DESTROY_FORCE = 1 << 0, + BE_DESTROY_ORIGIN = 1 << 1, } be_destroy_opt_t; int be_destroy(libbe_handle_t *, const char *, int); @@ -102,7 +104,7 @@ int be_destroy(libbe_handle_t *, const char *, int); typedef enum { BE_MNT_FORCE = 1 << 0, - BE_MNT_DEEP = 1 << 1, + BE_MNT_DEEP = 1 << 1, } be_mount_opt_t; int be_mount(libbe_handle_t *, char *, char *, int, char *); @@ -118,7 +120,7 @@ void libbe_print_on_error(libbe_handle_t *, bool); int be_root_concat(libbe_handle_t *, const char *, char *); int be_validate_name(libbe_handle_t * __unused, const char *); int be_validate_snap(libbe_handle_t *, const char *); -bool be_exists(libbe_handle_t *, char *); +int be_exists(libbe_handle_t *, char *); int be_export(libbe_handle_t *, const char *, int fd); int be_import(libbe_handle_t *, const char *, int fd); Modified: stable/11/lib/libbe/be_access.c ============================================================================== --- head/lib/libbe/be_access.c Sat Aug 11 23:50:09 2018 (r337663) +++ stable/11/lib/libbe/be_access.c Sat Apr 20 04:16:51 2019 (r346429) @@ -3,6 +3,7 @@ * * Copyright (c) 2017 Kyle J. Kneitinger * Copyright (c) 2018 Kyle Evans + * Copyright (c) 2019 Wes Maag * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,6 +39,14 @@ struct be_mountcheck_info { char *name; }; +struct be_mount_info { + libbe_handle_t *lbh; + const char *be; + const char *mountpoint; + int mntflags; + int deepmount; +}; + static int be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) { @@ -51,23 +60,124 @@ be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) return (0); if (strcmp(mountpoint, info->path) == 0) { info->name = strdup(zfs_get_name(zfs_hdl)); + free(mountpoint); return (1); } + free(mountpoint); return (0); } /* + * Called from be_mount, uses the given zfs_handle and attempts to + * mount it at the passed mountpoint. If the deepmount flag is set, continue + * calling the function for each child dataset. + */ +static int +be_mount_iter(zfs_handle_t *zfs_hdl, void *data) +{ + int err; + char *mountpoint; + char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN]; + struct be_mount_info *info; + + info = (struct be_mount_info *)data; + + if (zfs_is_mounted(zfs_hdl, &mountpoint)) { + free(mountpoint); + return (0); + } + + if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF) + return (0); + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, BE_MAXPATHLEN, + NULL, NULL, 0, 1)) + return (1); + + if (strcmp("none", zfs_mnt) != 0) { + char opt = '\0'; + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***