From e7771f9e4a164c283346f7d399eb7f1c7175f7c8 Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Thu, 19 Jul 2018 17:34:58 +0000 Subject: [PATCH 001/322] Import upline security patch: Fix PTK rekeying to generate a new ANonce. This is also upline git commit 0adc9b28b39d414d5febfff752f6a1576f785c85. Obtained from: https://w1.fi/security/2017-1/\ rebased-v2.6-0005-Fix-PTK-rekeying-to-\ generate-a-new-ANonce.patch --- src/ap/wpa_auth.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 707971d06f21..bf10cc1646f7 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1901,6 +1901,21 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) } +static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm) +{ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, + "WPA: Failed to get random data for ANonce"); + sm->Disconnect = TRUE; + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce, + WPA_NONCE_LEN); + sm->TimeoutCtr = 0; + return 0; +} + + SM_STATE(WPA_PTK, INITPMK) { u8 msk[2 * PMK_LEN]; @@ -2458,9 +2473,12 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, AUTHENTICATION); else if (sm->ReAuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION2); - else if (sm->PTKRequest) - SM_ENTER(WPA_PTK, PTKSTART); - else switch (sm->wpa_ptk_state) { + else if (sm->PTKRequest) { + if (wpa_auth_sm_ptk_update(sm) < 0) + SM_ENTER(WPA_PTK, DISCONNECTED); + else + SM_ENTER(WPA_PTK, PTKSTART); + } else switch (sm->wpa_ptk_state) { case WPA_PTK_INITIALIZE: break; case WPA_PTK_DISCONNECT: From b87b1451cb190c2ae09f46711933aa2938405396 Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Thu, 19 Jul 2018 17:37:13 +0000 Subject: [PATCH 002/322] Import upline security patch: TDLS: Reject TPK-TK reconfiguration. This is also upline git commmit ff89af96e5a35c86f50330d2b86c18323318a60c. Obtained from: https://w1.fi/security/2017-1/\ rebased-v2.6-0006-TDLS-Reject-TPK-TK-\ reconfiguration.patch --- src/rsn_supp/tdls.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index e4241681842a..9eb973860049 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -112,6 +112,7 @@ struct wpa_tdls_peer { u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ } tpk; int tpk_set; + int tk_set; /* TPK-TK configured to the driver */ int tpk_success; int tpk_in_progress; @@ -192,6 +193,20 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) u8 rsc[6]; enum wpa_alg alg; + if (peer->tk_set) { + /* + * This same TPK-TK has already been configured to the driver + * and this new configuration attempt (likely due to an + * unexpected retransmitted frame) would result in clearing + * the TX/RX sequence number which can break security, so must + * not allow that to happen. + */ + wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR + " has already been configured to the driver - do not reconfigure", + MAC2STR(peer->addr)); + return -1; + } + os_memset(rsc, 0, 6); switch (peer->cipher) { @@ -209,12 +224,15 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) return -1; } + wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR, + MAC2STR(peer->addr)); if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " "driver"); return -1; } + peer->tk_set = 1; return 0; } @@ -696,7 +714,7 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) peer->cipher = 0; peer->qos_info = 0; peer->wmm_capable = 0; - peer->tpk_set = peer->tpk_success = 0; + peer->tk_set = peer->tpk_set = peer->tpk_success = 0; peer->chan_switch_enabled = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); @@ -1159,6 +1177,7 @@ static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, wpa_tdls_peer_free(sm, peer); return -1; } + peer->tk_set = 0; /* A new nonce results in a new TK */ wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", peer->inonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); @@ -1751,6 +1770,19 @@ static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, } +static int tdls_nonce_set(const u8 *nonce) +{ + int i; + + for (i = 0; i < WPA_NONCE_LEN; i++) { + if (nonce[i]) + return 1; + } + + return 0; +} + + static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { @@ -2004,7 +2036,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; - if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 || + !tdls_nonce_set(peer->inonce)) { /* * There is no point in updating the RNonce for every obtained * TPK M1 frame (e.g., retransmission due to timeout) with the @@ -2020,6 +2053,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, "TDLS: Failed to get random data for responder nonce"); goto error; } + peer->tk_set = 0; /* A new nonce results in a new TK */ } #if 0 From 6bcebda49663a58068118b1cacc4aecd27e2e54f Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Thu, 19 Jul 2018 17:46:33 +0000 Subject: [PATCH 003/322] Import upline security patch: WNM: Ignore WNM-Sleep Mode Request in wnm_sleep_mode=0 case. This is also upline git commit 114f2830d2c2aee6db23d48240e93415a256a37c. Obtained from: https://w1.fi/security/2017-1/\ rebased-v2.6-0007-WNM-Ignore-WNM-Sleep-Mode-\ Response-without-pending-r.patch --- wpa_supplicant/wnm_sta.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 1b3409c1fb71..67a07ff7b1e7 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -260,7 +260,7 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, if (!wpa_s->wnmsleep_used) { wpa_printf(MSG_DEBUG, - "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode has not been used in this association"); + "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); return; } @@ -299,6 +299,8 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, return; } + wpa_s->wnmsleep_used = 0; + if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " From 63696d5028b91f9fa4607f778587311502810783 Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Thu, 19 Jul 2018 17:49:39 +0000 Subject: [PATCH 004/322] Import upline security patch: FILS: Do not allow multiple (Re)Association Response frames. This is also upline git commit e760851176c77ae6de19821bb1d5bf3ae2cb5187. Obtained from: https://w1.fi/security/2017-1/\ rebased-v2.6-0008-FT-Do-not-allow-multiple-\ Reassociation-Response-fram.patch --- src/rsn_supp/wpa.c | 3 +++ src/rsn_supp/wpa_ft.c | 8 ++++++++ src/rsn_supp/wpa_i.h | 1 + 3 files changed, 12 insertions(+) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 0550a412fcc7..2a53c6f4a55f 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -2440,6 +2440,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_IEEE80211R + sm->ft_reassoc_completed = 0; +#endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ wpa_sm_drop_sa(sm); diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 205793e7f43a..d45bb4585e50 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -153,6 +153,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, u16 capab; sm->ft_completed = 0; + sm->ft_reassoc_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + sm->r0kh_id_len + ric_ies_len + 100; @@ -681,6 +682,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } + if (sm->ft_reassoc_completed) { + wpa_printf(MSG_DEBUG, "FT: Reassociation has already been completed for this FT protocol instance - ignore unexpected retransmission"); + return 0; + } + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; @@ -781,6 +787,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } + sm->ft_reassoc_completed = 1; + if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) return -1; diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 41f371f26c69..56f88dcdd899 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -128,6 +128,7 @@ struct wpa_sm { size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; int ft_completed; + int ft_reassoc_completed; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; From 28f16a0f1985b32a97075a540236e8abebaeadf1 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 24 Jul 2018 13:17:40 +0000 Subject: [PATCH 005/322] Import libbe(3)/be(1) from socsvn/soc2017/kneitinger/libbe-head --- lib/libbe/Makefile | 53 +++ lib/libbe/be.c | 876 +++++++++++++++++++++++++++++++++++++++ lib/libbe/be.h | 113 +++++ lib/libbe/be_access.c | 132 ++++++ lib/libbe/be_error.c | 115 +++++ lib/libbe/be_impl.h | 59 +++ lib/libbe/be_info.c | 245 +++++++++++ lib/libbe/libbe.3 | 192 +++++++++ sbin/Makefile | 1 + sbin/be/Makefile | 10 + sbin/be/be.1 | 220 ++++++++++ sbin/be/be.c | 664 +++++++++++++++++++++++++++++ share/mk/src.libnames.mk | 4 + 13 files changed, 2684 insertions(+) create mode 100644 lib/libbe/Makefile create mode 100644 lib/libbe/be.c create mode 100644 lib/libbe/be.h create mode 100644 lib/libbe/be_access.c create mode 100644 lib/libbe/be_error.c create mode 100644 lib/libbe/be_impl.h create mode 100644 lib/libbe/be_info.c create mode 100644 lib/libbe/libbe.3 create mode 100644 sbin/be/Makefile create mode 100644 sbin/be/be.1 create mode 100644 sbin/be/be.c diff --git a/lib/libbe/Makefile b/lib/libbe/Makefile new file mode 100644 index 000000000000..cbb40af821c8 --- /dev/null +++ b/lib/libbe/Makefile @@ -0,0 +1,53 @@ +# $FreeBSD$ +# +# Copyright (c) 2010 Hans Petter Selasky. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +PACKAGE= lib${LIB} +LIB= be +SHLIB_MAJOR= 1 +SHLIB_MINOR= 0 +SRCS= be.c be_access.c be_error.c be_info.c +INCS= be.h +MAN= libbe.3 + +WARNS?= 1 + +LIBADD+= zfs +LIBADD+= nvpair + +CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common +CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris +CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include +CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem +CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common +CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs +CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs +CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common +CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head +LDFLAGS+= -v + +CFLAGS+= -DNEED_SOLARIS_BOOLEAN + +.include diff --git a/lib/libbe/be.c b/lib/libbe/be.c new file mode 100644 index 000000000000..f286cc37b11f --- /dev/null +++ b/lib/libbe/be.c @@ -0,0 +1,876 @@ +/* + * be.c + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "be.h" +#include "be_impl.h" + +/* + * Initializes the libbe context to operate in the root boot environment + * dataset, for example, zroot/ROOT. + */ +libbe_handle_t * +libbe_init(void) +{ + char buf[BE_MAXPATHLEN]; + struct stat sb; + dev_t root_dev, boot_dev; + libbe_handle_t *lbh; + char *pos; + + // TODO: use errno here?? + + /* Verify that /boot and / are mounted on the same filesystem */ + if (stat("/", &sb) != 0) { + return (NULL); + } + + root_dev = sb.st_dev; + + if (stat("/boot", &sb) != 0) { + return (NULL); + } + + boot_dev = sb.st_dev; + + if (root_dev != boot_dev) { + fprintf(stderr, "/ and /boot not on same device, quitting\n"); + return (NULL); + } + + if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) { + return (NULL); + } + + if ((lbh->lzh = libzfs_init()) == NULL) { + free(lbh); + return (NULL); + } + + /* Obtain path to active boot environment */ + if ((kenv(KENV_GET, "zfs_be_active", lbh->active, + BE_MAXPATHLEN)) == -1) { + libzfs_fini(lbh->lzh); + free(lbh); + return (NULL); + } + + /* Remove leading 'zfs:' if present, otherwise use value as-is */ + if ((pos = strrchr(lbh->active, ':')) != NULL) { + strncpy(lbh->active, pos + sizeof(char), BE_MAXPATHLEN); + } + + /* Obtain path to boot environment root */ + if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) { + libzfs_fini(lbh->lzh); + free(lbh); + return (NULL); + } + + /* Remove leading 'zfs:' if present, otherwise use value as-is */ + if ((pos = strrchr(lbh->root, ':')) != NULL) { + strncpy(lbh->root, pos + sizeof(char), BE_MAXPATHLEN); + } + + return (lbh); +} + + +/* + * Free memory allocated by libbe_init() + */ +void +libbe_close(libbe_handle_t *lbh) +{ + libzfs_fini(lbh->lzh); + free(lbh); +} + + +/* + * 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 + * TODO: Test destroying a non active but mounted be + */ +int +be_destroy(libbe_handle_t *lbh, char *name, int options) +{ + zfs_handle_t *fs; + char path[BE_MAXPATHLEN]; + char *p = path; + int mounted; + int force = options & BE_DESTROY_FORCE; + + int err = BE_ERR_SUCCESS; + + be_root_concat(lbh, name, path); + printf("path: %s\n", path); + + if (strchr(name, '@') == NULL) { + if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) { + return (set_error(lbh, BE_ERR_NOENT)); + } + + if (strcmp(path, lbh->active) == 0) { + return (set_error(lbh, BE_ERR_DESTROYACT)); + } + + fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); + } 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); + } + + 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)) { + if (force) { + zfs_unmount(fs, NULL, 0); + } else { + return (set_error(lbh, BE_ERR_DESTROYMNT)); + } + } + + + // TODO: convert this to use zfs_iter_children first for deep bes + // XXX note: errno 16 (device busy) occurs when chilren are present + if ((err = zfs_destroy(fs, false)) != 0) { + fprintf(stderr, "delete failed errno: %d\n", errno); + } + + return (err); +} + + +int +be_snapshot(libbe_handle_t *lbh, char *source, char *snap_name, + bool recursive, char *result) +{ + char buf[BE_MAXPATHLEN]; + time_t rawtime; + int len, err; + + be_root_concat(lbh, source, buf); + + if (!be_exists(lbh, buf)) { + return (BE_ERR_NOENT); + } + + if (snap_name != NULL) { + strcat(buf, "@"); + strcat(buf, snap_name); + 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); + } + } + + if (err = zfs_snapshot(lbh->lzh, buf, recursive, NULL) != 0) { + switch (err) { + case EZFS_INVALIDNAME: + return (set_error(lbh, BE_ERR_INVALIDNAME)); + + default: + // TODO: elaborate return codes + return (set_error(lbh, BE_ERR_UNKNOWN)); + } + } + + return (BE_ERR_SUCCESS); +} + + +/* + * Create the boot environment specified by the name parameter + */ +int +be_create(libbe_handle_t *lbh, char *name) +{ + int err; + + err = be_create_from_existing(lbh, name, (char *)be_active_path(lbh)); + + return (set_error(lbh, err)); +} + + +static int +be_deep_clone_prop(int prop, void *cb) +{ + int err; + struct libbe_dccb *dccb = cb; + zprop_source_t src; + char pval[BE_MAXPATHLEN]; + char source[BE_MAXPATHLEN]; + + /* Skip some properties we don't want to touch */ + switch (prop) { + case ZFS_PROP_CANMOUNT: + return (ZPROP_CONT); + break; + } + + /* Don't copy readonly properties */ + if (zfs_prop_readonly(prop)) { + return (ZPROP_CONT); + } + + if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, + sizeof(pval), &src, (char *)&source, sizeof(source), false))) { + /* Just continue if we fail to read a property */ + return (ZPROP_CONT); + } + /* Only copy locally defined properties */ + if (src != ZPROP_SRC_LOCAL) { + return (ZPROP_CONT); + } + + nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); + + return (ZPROP_CONT); +} + +static int +be_deep_clone(zfs_handle_t *ds, void *data) +{ + int err; + char be_path[BE_MAXPATHLEN]; + char snap_path[BE_MAXPATHLEN]; + char mp[BE_MAXPATHLEN]; + const char *dspath; + char *dsname; + zfs_handle_t *snap_hdl; + nvlist_t *props; + struct libbe_deep_clone sdc; + struct libbe_deep_clone *isdc = (struct libbe_deep_clone *)data; + struct libbe_dccb dccb; + + dspath = zfs_get_name(ds); + if ((dsname = strrchr(dspath, '/')) == NULL) { + return (BE_ERR_UNKNOWN); + } + dsname++; + if (isdc->bename == NULL) { + snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); + } else { + snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); + } + snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); + + if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) { + return (set_error(isdc->lbh, BE_ERR_EXISTS)); + } + + if ((snap_hdl = + zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) { + return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); + } + + nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); + nvlist_add_string(props, "canmount", "noauto"); + + 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)) { + switch (err) { + case EZFS_SUCCESS: + err = BE_ERR_SUCCESS; + break; + default: + err = BE_ERR_ZFSCLONE; + break; + } + } + + nvlist_free(props); + zfs_close(snap_hdl); + + 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); +} + +/* + * Create the boot environment from pre-existing snapshot + */ +int +be_create_from_existing_snap(libbe_handle_t *lbh, char *name, char *snap) +{ + int err; + char be_path[BE_MAXPATHLEN]; + char snap_path[BE_MAXPATHLEN]; + char *parentname, *bename, *snapname; + zfs_handle_t *parent_hdl; + struct libbe_deep_clone sdc; + + if (err = be_validate_name(lbh, name)) { + return (set_error(lbh, err)); + } + + if (err = be_root_concat(lbh, snap, snap_path)) { + return (set_error(lbh, err)); + } + + if (err = be_validate_snap(lbh, snap_path)) { + return (set_error(lbh, err)); + } + + if (err = be_root_concat(lbh, name, be_path)) { + return (set_error(lbh, err)); + } + + if ((bename = strrchr(name, '/')) == NULL) { + bename = name; + } else { + bename++; + } + if ((parentname = strdup(snap_path)) == NULL) { + err = BE_ERR_UNKNOWN; + return (set_error(lbh, err)); + } + snapname = strchr(parentname, '@'); + if (snapname == NULL) { + err = BE_ERR_UNKNOWN; + return (set_error(lbh, err)); + } + *snapname = '\0'; + snapname++; + + sdc.lbh = lbh; + sdc.bename = bename; + sdc.snapname = snapname; + sdc.be_root = lbh->root; + + parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); + err = be_deep_clone(parent_hdl, &sdc); + + return (set_error(lbh, err)); +} + + +/* + * Create a boot environment from an existing boot environment + */ +int +be_create_from_existing(libbe_handle_t *lbh, char *name, char *old) +{ + int err; + char buf[BE_MAXPATHLEN]; + + if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) { + return (set_error(lbh, err)); + } + + err = be_create_from_existing_snap(lbh, name, (char *)buf); + + return (set_error(lbh, err)); +} + + +/* + * Verifies that a snapshot has a valid name, exists, and has a mountpoint of + * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon + * failure. Does not set the internal library error state. + */ +int +be_validate_snap(libbe_handle_t *lbh, char *snap_name) +{ + zfs_handle_t *zfs_hdl; + char buf[BE_MAXPATHLEN]; + char *delim_pos; + char *mountpoint; + int err = BE_ERR_SUCCESS; + + if (strlen(snap_name) >= BE_MAXPATHLEN) { + return (BE_ERR_PATHLEN); + } + + 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)) { + err = BE_ERR_INVORIGIN; + } + + if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) { + err = BE_ERR_INVORIGIN; + } + + zfs_close(zfs_hdl); + + return (err); +} + + +/* + * Idempotently appends the name argument to the root boot environment path + * and copies the resulting string into the result buffer (which is assumed + * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon + * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, + * or BE_ERR_INVALIDNAME if the name is a path that does not begin with + * zfs_be_root. Does not set internal library error state. + */ +int +be_root_concat(libbe_handle_t *lbh, char *name, char *result) +{ + size_t name_len, root_len; + + name_len = strlen(name); + root_len = strlen(lbh->root); + + /* Act idempotently; return be name if it is already a full path */ + if (strrchr(name, '/') != NULL) { + if (strstr(name, lbh->root) != name) { + return (BE_ERR_INVALIDNAME); + } + + if (name_len >= BE_MAXPATHLEN) { + return (BE_ERR_PATHLEN); + } + + strncpy(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, + name); + return (BE_ERR_SUCCESS); + } + + return (BE_ERR_PATHLEN); +} + + +/* + * 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. + * Does not set internal library error state. + */ +int +be_validate_name(libbe_handle_t *lbh, char *name) +{ + for (int i = 0; *name; i++) { + char c = *(name++); + if (isalnum(c) || (c == '-') || (c == '_') || + (c == '.')) { + continue; + } + return (BE_ERR_INVALIDNAME); + } + + return (BE_ERR_SUCCESS); +} + + +/* + * usage + */ +int +be_rename(libbe_handle_t *lbh, char *old, char *new) +{ + char full_old[BE_MAXPATHLEN]; + char full_new[BE_MAXPATHLEN]; + zfs_handle_t *zfs_hdl; + int err; + + + if (err = be_root_concat(lbh, old, full_old)) { + return (set_error(lbh, err)); + } + if (err = be_root_concat(lbh, new, full_new)) { + return (set_error(lbh, err)); + } + + if (be_validate_name(lbh, new)) { + return (BE_ERR_UNKNOWN); + // TODO set and return correct error + } + + // check if old is active be + if (strcmp(full_new, be_active_path(lbh)) == 0) { + return (BE_ERR_UNKNOWN); + // TODO set and return correct error + } + + if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) { + return (BE_ERR_UNKNOWN); + // TODO set and return correct error + } + + if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) { + return (BE_ERR_UNKNOWN); + // TODO set and return correct error + } + + // TODO: what about mounted bes? + // - if mounted error out unless a force flag is set? + + + if ((zfs_hdl = zfs_open(lbh->lzh, full_old, + ZFS_TYPE_FILESYSTEM)) == NULL) { + return (BE_ERR_UNKNOWN); + // TODO set and return correct error + } + + + // recurse, nounmount, forceunmount + struct renameflags flags = { 0, 0, 0 }; + + // TODO: error log on this call + err = zfs_rename(zfs_hdl, NULL, full_new, flags); + + zfs_close(zfs_hdl); + + return (set_error(lbh, err)); +} + + +int +be_export(libbe_handle_t *lbh, char *bootenv, int fd) +{ + char snap_name[BE_MAXPATHLEN]; + char buf[BE_MAXPATHLEN]; + zfs_handle_t *zfs; + int err; + + if (err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) { + // TODO error handle + return (-1); + } + + be_root_concat(lbh, snap_name, buf); + + if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) { + return (BE_ERR_ZFSOPEN); + } + + err = zfs_send_one(zfs, NULL, fd, 0); + return (err); +} + + +int +be_import(libbe_handle_t *lbh, char *bootenv, int fd) +{ + char buf[BE_MAXPATHLEN]; + time_t rawtime; + nvlist_t *props; + zfs_handle_t *zfs; + int err, len; + + // TODO: this is a very likely name for someone to already have used + if (err = be_root_concat(lbh, "be_import_temp", buf)) { + // TODO error handle + return (-1); + } + + time(&rawtime); + len = strlen(buf); + strftime(buf + len, BE_MAXPATHLEN - len, + "@%F-%T", localtime(&rawtime)); + + + // lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { + if (err = lzc_receive(buf, NULL, NULL, false, fd)) { + /* TODO: go through libzfs_core's recv_impl and find returned + * errors and set appropriate BE_ERR + * edit: errors are not in libzfs_core, my assumption is + * that they use libzfs errors + * note: 17 is err for dataset already existing */ + return (err); + } + + if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) { + // TODO correct error + return (-1); + } + + 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_clone(zfs, buf, props); + zfs_close(zfs); + + nvlist_free(props); + + // TODO: recursively delete be_import_temp dataset + + return (err); +} + + +int +be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) +{ + char active[BE_MAXPATHLEN]; + char buf[BE_MAXPATHLEN]; + nvlist_t *props; + zfs_handle_t *zfs; + struct stat sb; + int err; + + /* Require absolute paths */ + if (*child_path != '/') { + /* TODO: create appropriate error */ + return (-1); + } + + strncpy(active, be_active_path(lbh), BE_MAXPATHLEN); + strcpy(buf, active); + + /* Create non-mountable parent dataset(s) */ + char *s = child_path; + for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { + size_t len = p - s; + strncat(buf, s, len); + + nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); + nvlist_add_string(props, "canmount", "off"); + nvlist_add_string(props, "mountpoint", "none"); + zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); + nvlist_free(props); + } + + + /* Path does not exist as a descendent of / yet */ + int pos = strlen(active); + + /* TODO: Verify that resulting str is less than BE_MAXPATHLEN */ + strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); + + + if (stat(child_path, &sb) != 0) { + /* Verify that error is ENOENT */ + if (errno != 2) { + /* TODO: create appropriate error */ + return (-1); + } + + nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); + nvlist_add_string(props, "canmount", "noauto"); + nvlist_add_string(props, "mountpoint", child_path); + + // create + if (err = + zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) { + /* TODO handle error */ + return (-1); + } + nvlist_free(props); + + if ((zfs = + zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) { + /* TODO handle error */ + return (-1); + } + + // set props + if (err = zfs_prop_set(zfs, "canmount", "noauto")) { + /* TODO handle error */ + return (-1); + } + } else if (cp_if_exists) { + /* Path is already a descendent of / and should be copied */ + + + + // TODO + + /* + * Establish if the existing path is a zfs dataset or just + * the subdirectory of one + */ + + + // TODO: use mktemp + long int snap_name = random(); + + snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); + + if (err = zfs_snapshot(lbh->lzh, buf, false, NULL)) { + // TODO correct error + return (-1); + } + + // clone + if ((zfs = + zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) { + // TODO correct error + return (-1); + } + + if (err = zfs_clone(zfs, active, NULL)) { + // TODO correct error + return (-1); + } + + + // set props + } else { + /* TODO: error code for exists, but not cp? */ + return (-1); + } + + + return (BE_ERR_SUCCESS); +} + + +int +be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) +{ + char be_path[BE_MAXPATHLEN]; + char buf[BE_MAXPATHLEN]; + zpool_handle_t *zph; + uint64_t pool_guid; + uint64_t vdev_guid; + int zfs_fd; + int len; + 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 (temporary) { + // TODO: give proper attribution to author(s) of zfsbootcfg + // for this snippet + + if (kenv(KENV_GET, "vfs.zfs.boot.primary_pool", buf, + sizeof(buf)) <= 0) { + return (1); + } + pool_guid = strtoumax(buf, NULL, 10); + if (pool_guid == 0) { + return (1); + } + + if (kenv(KENV_GET, "vfs.zfs.boot.primary_vdev", buf, + sizeof(buf)) <= 0) { + return (1); + } + vdev_guid = strtoumax(buf, NULL, 10); + if (vdev_guid == 0) { + return (1); + } + + /* Expected format according to zfsbootcfg(8) man */ + strcpy(buf, "zfs:"); + strcat(buf, be_path); + strcat(buf, ":"); + + if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, buf) != 0) { + perror("ZFS_IOC_NEXTBOOT failed"); + return (1); + } + + return (BE_ERR_SUCCESS); + } else { + /* Obtain bootenv zpool */ + strncpy(buf, be_path, BE_MAXPATHLEN); + *(strchr(buf, '/')) = '\0'; + + if ((zph = zpool_open(lbh->lzh, buf)) == NULL) { + // TODO: create error for this + return (-1); + } + printf("asdf\n"); + + err = zpool_set_prop(zph, "bootfs", be_path); + zpool_close(zph); + + switch (err) { + case 0: + return (BE_ERR_SUCCESS); + + default: + // TODO correct errors + return (-1); + } + } +} diff --git a/lib/libbe/be.h b/lib/libbe/be.h new file mode 100644 index 000000000000..8a352ad9a642 --- /dev/null +++ b/lib/libbe/be.h @@ -0,0 +1,113 @@ +/* + * be.h + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBBE_H +#define _LIBBE_H + +#include + +#define BE_MAXPATHLEN 512 + +typedef struct libbe_handle libbe_handle_t; + +typedef enum be_error { + BE_ERR_SUCCESS = 0, /* No error */ + BE_ERR_INVALIDNAME, /* invalid boot env name */ + BE_ERR_EXISTS, /* boot env name already taken */ + BE_ERR_NOENT, /* boot env doesn't exist */ + BE_ERR_PERMS, /* insufficient permissions */ + BE_ERR_DESTROYACT, /* cannot destroy active boot env */ + BE_ERR_DESTROYMNT, /* destroying a mounted be requires force */ + BE_ERR_PATHLEN, /* provided name exceeds maximum length limit */ + BE_ERR_INVORIGIN, /* snapshot origin's 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 */ + BE_ERR_ZFSOPEN, /* calling zfs_open() failed */ + BE_ERR_ZFSCLONE, /* error when calling zfs_clone to create be */ + BE_ERR_UNKNOWN, /* unknown error */ +} be_error_t; + + +/* Library handling functions: be.c */ +libbe_handle_t *libbe_init(void); +void libbe_close(libbe_handle_t *); + +/* Bootenv information functions: be_info.c */ +const char *be_active_name(libbe_handle_t *); +const char *be_active_path(libbe_handle_t *); +const char *be_root_path(libbe_handle_t *); + +/* nvlist_t *be_get_bootenv_props(libbe_handle_t *); */ + +int be_activate(libbe_handle_t *, char *, bool); + +/* Bootenv creation functions */ +int be_create(libbe_handle_t *, char *); +int be_create_from_existing(libbe_handle_t *, char *, char *); +int be_create_from_existing_snap(libbe_handle_t *, char *, char *); +int be_snapshot(libbe_handle_t *, char *, char *, bool, char *); + +/* Bootenv manipulation functions */ +int be_rename(libbe_handle_t *, char *, char *); + +/* Bootenv removal functions */ + +typedef enum { + BE_DESTROY_FORCE = 1 << 0, +} be_destroy_opt_t; + +int be_destroy(libbe_handle_t *, char *, int); + +/* Bootenv mounting functions: be_access.c */ + +typedef enum { + BE_MNT_FORCE = 1 << 0, + BE_MNT_DEEP = 1 << 1, +} be_mount_opt_t; + +int be_mount(libbe_handle_t *, char *, char *, int, char *); +int be_unmount(libbe_handle_t *, char *, int); + +/* Error related functions: be_error.c */ +int libbe_errno(libbe_handle_t *); +const char *libbe_error_description(libbe_handle_t *); +void libbe_print_on_error(libbe_handle_t *, bool); + +/* Utility Functions */ +int be_root_concat(libbe_handle_t *, char *, char *); +int be_validate_name(libbe_handle_t *, char *); +int be_validate_snap(libbe_handle_t *, char *); +bool be_exists(libbe_handle_t *, char *); + +int be_export(libbe_handle_t *, char *, int fd); +int be_import(libbe_handle_t *, char *, int fd); + +int be_add_child(libbe_handle_t *, char *, bool); + +#endif /* _LIBBE_H */ diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c new file mode 100644 index 000000000000..3fa5b04c76f5 --- /dev/null +++ b/lib/libbe/be_access.c @@ -0,0 +1,132 @@ +/* + * be_access.c + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "be.h" +#include "be_impl.h" + +/* + * usage + */ +int +be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, + char *result_loc) +{ + char be[BE_MAXPATHLEN]; + char mnt_temp[BE_MAXPATHLEN]; + zfs_handle_t *zfs_hdl; + char *path; + int mntflags; + int err; + + if (err = be_root_concat(lbh, bootenv, be)) { + return (set_error(lbh, err)); + } + + if (!be_exists(lbh, bootenv)) { + return (set_error(lbh, BE_ERR_NOENT)); + } + + if (is_mounted(lbh->lzh, be, &path)) { + return (set_error(lbh, BE_ERR_MOUNTED)); + } + + + mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; + + /* Create mountpoint if it is not specified */ + if (mountpoint == NULL) { + strcpy(mnt_temp, "/tmp/be_mount.XXXX"); + if (mkdtemp(mnt_temp) == NULL) { + // TODO: create error for this + return (set_error(lbh, BE_ERR_UNKNOWN)); + } + } + + char opt = '\0'; + if (err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint, + mntflags, MNTTYPE_ZFS, NULL, 0, &opt, 1)) { + // TODO: zmount returns the nmount error, look into what kind of + // errors we can report from that + return (set_error(lbh, BE_ERR_UNKNOWN)); + } + + if (result_loc != NULL) { + strcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint); + } + + return (BE_ERR_SUCCESS); +} + + +/* + * usage + */ +int +be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) +{ + int err, mntflags; + char be[BE_MAXPATHLEN]; + struct statfs *mntbuf; + int mntsize; + char *mntpath; + + if (err = be_root_concat(lbh, bootenv, be)) { + return (set_error(lbh, err)); + } + + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + // TODO correct error + return (set_error(lbh, BE_ERR_NOMOUNT)); + } + + mntpath = NULL; + for (int i = 0; i < mntsize; ++i) { + /* 0x000000de is the type number of zfs */ + if (mntbuf[i].f_type != 0x000000de) { + continue; + } + + if (strcmp(mntbuf[i].f_mntfromname, be) == 0) { + mntpath = mntbuf[i].f_mntonname; + break; + } + } + + if (mntpath == NULL) { + return (set_error(lbh, BE_ERR_NOMOUNT)); + } + + mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; + + if (err = unmount(mntpath, mntflags)) { + // TODO correct error + return (set_error(lbh, BE_ERR_NOMOUNT)); + } + + return (set_error(lbh, BE_ERR_SUCCESS)); +} diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c new file mode 100644 index 000000000000..af5f339dc485 --- /dev/null +++ b/lib/libbe/be_error.c @@ -0,0 +1,115 @@ +/* + * be_error.c + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "be.h" +#include "be_impl.h" + +/* + * Usage + */ +int +libbe_errno(libbe_handle_t *lbh) +{ + return (lbh->error); +} + + +const char * +libbe_error_description(libbe_handle_t *lbh) +{ + switch (lbh->error) { + case BE_ERR_INVALIDNAME: + return ("invalid boot environment name"); + + case BE_ERR_EXISTS: + return ("boot environment name already taken"); + + case BE_ERR_NOENT: + return ("specified boot environment does not exist"); + + case BE_ERR_PERMS: + return ("insufficient permissions"); + + case BE_ERR_DESTROYACT: + return ("cannot destroy active boot environment"); + + case BE_ERR_DESTROYMNT: + return ("cannot destroy mounted boot env unless forced"); + + case BE_ERR_PATHLEN: + return ("provided path name exceeds maximum length limit"); + + case BE_ERR_INVORIGIN: + return ("snapshot origin's mountpoint is not \"/\""); + + case BE_ERR_NOORIGIN: + return ("could not open snapshot's origin"); + + case BE_ERR_MOUNTED: + return ("boot environment is already mounted"); + + case BE_ERR_NOMOUNT: + return ("boot environment is not mounted"); + + case BE_ERR_ZFSOPEN: + return ("calling zfs_open() failed"); + + case BE_ERR_ZFSCLONE: + return ("error when calling zfs_clone() to create boot env"); + + case BE_ERR_UNKNOWN: + return ("unknown error"); + + default: + assert(lbh->error == BE_ERR_SUCCESS); + return ("no error"); + } +} + + +void +libbe_print_on_error(libbe_handle_t *lbh, bool val) +{ + lbh->print_on_err = val; + libzfs_print_on_error(lbh->lzh, val); +} + + +int +set_error(libbe_handle_t *lbh, be_error_t err) +{ + // TODO: should the old error be overwritten or no? + + lbh->error = err; + + if (lbh->print_on_err && (err != BE_ERR_SUCCESS)) { + fprintf(stderr, "%s\n", libbe_error_description(lbh)); + } + + return (err); +} diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h new file mode 100644 index 000000000000..8766e6d09918 --- /dev/null +++ b/lib/libbe/be_impl.h @@ -0,0 +1,59 @@ +/* + * be_impl.h + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBBE_IMPL_H +#define _LIBBE_IMPL_H + +#include + +#include "be.h" + + +struct libbe_handle { + libzfs_handle_t *lzh; + char root[BE_MAXPATHLEN]; + char active[BE_MAXPATHLEN]; + be_error_t error; + bool print_on_err; +}; + +struct libbe_deep_clone { + libbe_handle_t *lbh; + char *bename; + char *snapname; + char *be_root; +}; + +struct libbe_dccb { + zfs_handle_t *zhp; + nvlist_t *props; +}; + +int set_error(libbe_handle_t *, be_error_t); + +#endif /* _LIBBE_IMPL_H */ diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c new file mode 100644 index 000000000000..747155916a95 --- /dev/null +++ b/lib/libbe/be_info.c @@ -0,0 +1,245 @@ +/* + * be_info.c + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "be.h" +#include "be_impl.h" + +typedef struct prop_data { + nvlist_t *list; + libbe_handle_t *lbh; +} prop_data_t; + +static int prop_list_builder_cb(zfs_handle_t *, void *); +static int prop_list_builder(prop_data_t *); + +/* + * Returns the name of the active boot environment + */ +const char * +be_active_name(libbe_handle_t *lbh) +{ + return (strrchr(lbh->active, '/') + sizeof(char)); +} + + +/* + * Returns full path of the active boot environment + */ +const char * +be_active_path(libbe_handle_t *lbh) +{ + return (lbh->active); +} + + +/* + * Returns the path of the boot environment root dataset + */ +const char * +be_root_path(libbe_handle_t *lbh) +{ + return (lbh->root); +} + + +/* + * Returns an nvlist of the bootenv's properties + * TODO: the nvlist should be passed as a param and ints should return status + */ +nvlist_t * +be_get_bootenv_props(libbe_handle_t *lbh) +{ + prop_data_t data; + + data.lbh = lbh; + prop_list_builder(&data); + + return (data.list); +} + + +/* + * Internal callback function used by zfs_iter_filesystems. For each dataset in + * the bootenv root, populate an nvlist_t of its relevant properties. + * TODO: should any other properties be included? + */ +static int +prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) +{ + /* + * TODO: + * some system for defining constants for the nvlist keys + * error checking + */ + + char buf[512]; + prop_data_t *data; + boolean_t mounted, active, nextboot; + + + data = (prop_data_t *)data_p; + + + nvlist_t *props; + + libbe_handle_t *lbh = data->lbh; + + + nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); + + const char *dataset = zfs_get_name(zfs_hdl); + nvlist_add_string(props, "dataset", dataset); + + const char *name = strrchr(dataset, '/') + 1; + nvlist_add_string(props, "name", name); + + + mounted = zfs_prop_get_int(zfs_hdl, ZFS_PROP_MOUNTED); + nvlist_add_boolean_value(props, "mounted", mounted); + + // TODO: NOT CORRECT! Must use is_mounted + if (mounted) { + zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512, + NULL, NULL, 0, 1); + nvlist_add_string(props, "mountpoint", buf); + } + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "origin", buf); + } + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "creation", buf); + } + + nvlist_add_boolean_value(props, "active", + (strcmp(be_active_path(lbh), dataset) == 0)); + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "used", buf); + } + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "usedds", buf); + } + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "usedsnap", buf); + } + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "usedrefreserv", buf); + } + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512, + NULL, NULL, 0, 1)) { + nvlist_add_string(props, "referenced", buf); + } + + /* TODO figure out how to read nextboot (set in libzfs_pool.c) */ + + nvlist_add_nvlist(data->list, name, props); + + return (0); +} + + +/* + * Updates the properties of each bootenv in the libbe handle + * TODO: rename to be_proplist_update + * TODO: ensure that this is always consistent (run after adds, deletes, + * renames,etc + */ +static int +prop_list_builder(prop_data_t *data) +{ + zfs_handle_t *root_hdl; + + if (nvlist_alloc(&(data->list), NV_UNIQUE_NAME, KM_SLEEP) != 0) { + /* TODO: actually handle error */ + return (1); + } + + + if ((root_hdl = + zfs_open(data->lbh->lzh, data->lbh->root, + ZFS_TYPE_FILESYSTEM)) == NULL) { + return (BE_ERR_ZFSOPEN); + } + + // TODO: some error checking here + zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data); + + zfs_close(root_hdl); + + return (0); +} + + +/* + * frees property list and its children + */ +void +be_prop_list_free(nvlist_t *be_list) +{ + nvlist_t *prop_list; + + nvpair_t *be_pair = nvlist_next_nvpair(be_list, NULL); + + if (nvpair_value_nvlist(be_pair, &prop_list) == 0) { + nvlist_free(prop_list); + } + + while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) { + if (nvpair_value_nvlist(be_pair, &prop_list) == 0) { + nvlist_free(prop_list); + } + } +} + + +/* + * Usage + */ +bool +be_exists(libbe_handle_t *lbh, char *be) +{ + char buf[BE_MAXPATHLEN]; + + be_root_concat(lbh, be, buf); + + // TODO: check mountpoint prop and see if its /, AND that result with below + // expression + return (zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET)); +} diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 new file mode 100644 index 000000000000..191840b60338 --- /dev/null +++ b/lib/libbe/libbe.3 @@ -0,0 +1,192 @@ +.\" +.\" Copyright (c) 2017 Kyle Kneitinger +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" This manual page is based on the mp(3X) manual page from Sun Release +.\" 4.1, dated 7 September 1989. It's an old, crufty, and relatively ugly +.\" manual page, but it does document what appears to be the "traditional" +.\" libmp interface. +.\" +.\" $FreeBSD$ +.\" +.Dd August 28, 2017 +.Dt LIBBE 3 +.Os +.Sh NAME +.Nm libbe +.Nd library for creating, destroying and modifying ZFS boot environments. +.Sh SYNOPSIS +.In be.h +.Pp +Function prototypes are given in the main body of the text. +.Pp +Applications using this interface must be linked with +.Fl l Ns Ar be +.Sh DESCRIPTION +.Pp +.Nm +interfaces with libzfs to provide a set of functions for various operations +regarding ZFS boot environments including "deep" boot environments in which +a boot environments has child datasets. +.Pp +A context structure is passed to each function, allowing for a small amount +of state to be retained, such as errors from previous operations. +.\" TODO: describe break on err functionality +.Pp +.Ft "libbe_handle_t *" Ns +.Fn libbe_init "void" ; +.Pp +.Ft void +.Fn libbe_close "libbe_handle_t *" ; +.Pp +.Ft "const char *" Ns +.Fn be_active_name "libbe_handle_t *" ; +.Pp +.Ft "const char *" Ns +.Fn be_active_path "libbe_handle_t *" ; +.Pp +.Ft "const char *" Ns +.Fn be_root_path "libbe_handle_t *" ; +.Pp +.Ft "nvlist_t *" Ns +.Fn libbe_handle_t "libbe_handle_t " ; +.Pp +.Ft int +.Fn be_create "libbe_handle_t *, char *" ; +.Pp +.Ft int +.Fn be_create_from_existing "libbe_handle_t *, char *, char *" ; +.Pp +.Ft int +.Fn be_rename "libbe_handle_t *, char *, char *" ; +.Pp +.\" TODO: Write up of destroy options +.\" typedef enum { +.\" BE_DESTROY_FORCE = 1 << 0, +.\" } be_destroy_opt_t; +.Ft int +.Fn be_destroy "libbe_handle_t *, char *, int" ; +.Pp +.\" TODO: Write up of mount options +.\" typedef enum { +.\" BE_MNT_FORCE = 1 << 0, +.\" BE_MNT_DEEP = 1 << 1, +.\" } be_mount_opt_t; +.Ft int +.Fn be_mount "libbe_handle_t *, char *, char *, int" ; +.Pp +.Ft int +.Fn be_unmount "libbe_handle_t *, char *, int" ; +.Pp +.Ft int +.Fn libbe_errno "libbe_handle_t *" ; +.Pp +.Ft "const char *" Ns +.Fn libbe_error_description "libbe_handle_t *" ; +.Pp +.Ft void +.Fn libbe_print_on_error "libbe_handle_t *, bool" ; +.Pp +.Ft int +.Fn be_root_concat "libbe_handle_t *, char *, char *" ; +.Pp +.Ft int +.Fn be_validate_name "libbe_handle_t *, char *" ; +.Pp +.Ft int +.Fn be_validate_snap "libbe_handle_t *, char *" ; +.Pp +.Ft bool +.Fn be_exists "libbe_handle_t *, char *" ; +.Pp +.Ft int +.Fn be_export "libbe_handle_t *, char *, int fd" ; +.Pp +.Ft int +.Fn be_import "libbe_handle_t *, char *, int fd" ; +.Pp +.Ft int +.Fn be_add_child "libbe_handle_t *, char *, bool" ; +.Pp +.\" .Ft void +.\" .Fn mp_mfree "MINT *mp" ; +.\" .Bd -ragged -offset indent +.\" .Fn mp_itom +.\" returns an +.\" .Vt MINT +.\" with the value of +.\" .Fa n . +.\" .Fn mp_xtom +.\" returns an +.\" .Vt MINT +.\" with the value of +.\" .Fa s , +.\" which is treated to be in hexadecimal. +.\" The return values from +.\" .Fn mp_itom +.\" and +.\" .Fn mp_xtom +.\" must be released with +.\" .Fn mp_mfree +.\" when they are no longer needed. +.\" .Fn mp_mtox +.\" returns a null-terminated hexadecimal string having the value of +.\" .Fa mp ; +.\" its return value must be released with +.\" .Fn free +.\" .Pq Xr free 3 +.\" when it is no longer needed. +.\" .Ed +.\" .Pp +.Sh DIAGNOSTICS +Upon error, one of the following values will be returned. +.\" TODO: make each entry on its own line. +.Bd -ragged -offset indent +BE_ERR_SUCCESS, +BE_ERR_INVALIDNAME, +BE_ERR_EXISTS, +BE_ERR_NOENT, +BE_ERR_PERMS, +BE_ERR_DESTROYACT, +BE_ERR_DESTROYMNT, +BE_ERR_PATHLEN, +BE_ERR_INVORIGIN, +BE_ERR_NOORIGIN, +BE_ERR_MOUNTED, +BE_ERR_NOMOUNT, +BE_ERR_ZFSOPEN, +BE_ERR_ZFSCLONE, +BE_ERR_UNKNOWN +.Ed +.Sh SEE ALSO +.Xr be 1 , +.Sh HISTORY +.Nm +and it's corresponding command, +.Xr be 3 , +were written as a 2017 Google Summer of Code project with Allan Jude serving +as a mentor. +.\" TODO: update when implementation complete. +.\" .Sh BUGS + diff --git a/sbin/Makefile b/sbin/Makefile index 1effb4f3cf15..13c1c0745bcc 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -86,6 +86,7 @@ SUBDIR.${MK_PF}+= pfctl SUBDIR.${MK_PF}+= pflogd SUBDIR.${MK_QUOTAS}+= quotacheck SUBDIR.${MK_ROUTED}+= routed +SUBDIR.${MK_ZFS}+= be SUBDIR.${MK_ZFS}+= zfsbootcfg SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/be/Makefile b/sbin/be/Makefile new file mode 100644 index 000000000000..3d3cc4e2314b --- /dev/null +++ b/sbin/be/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= be +WARNS?= 1 +MAN= be.1 + +LIBADD+= be +LIBADD+= nvpair + +.include diff --git a/sbin/be/be.1 b/sbin/be/be.1 new file mode 100644 index 000000000000..2dd7b3d9d633 --- /dev/null +++ b/sbin/be/be.1 @@ -0,0 +1,220 @@ +.\" +.\" be - Utility to manage Boot Environments on the ZFS filesystem +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" +.\" @(#)be.1 +.\" $FreeBSD$ +.\" +.Dd June 15, 2017 +.Dt BE 1 +.Os FreeBSD +.Sh NAME +.Nm be +.Nd Utility to manage Boot Environments on ZFS +.Sh SYNOPSIS +.Nm +activate +.Op Fl t +.Ao Ar beName Ac +.Nm +create +.Op Fl r +.Op Fl e Ar nonActiveBe | Fl e Ar beName@snapshot +.Ao Ar beName Ac +.Nm +create +.Op Fl r +.Ao Ar beName@snapshot Ac +.Nm +destroy +.Op Fl F +.Ao Ar beName | beName@snapshot Ac +.Nm +jail +.Ao Ar jailID | jailName Ac +.Ao Ar bootenv Ac +.Nm +list +.Op Fl a +.Op Fl D +.Op Fl H +.Op Fl s +.Nm +mount +.Ao Ar beName Ac +.Op mountpoint +.Nm +rename +.Ao Ar origBeName Ac +.Ao Ar newBeName Ac +.Nm +{ ujail | unjail } +.Ao Ar jailID | jailName Ac +.Ao Ar bootenv Ac +.Nm +{ umount | unmount } +.Op Fl f +.Ao Ar beName Ac +.Sh DESCRIPTION +The +.Nm +command is used to setup and interact with ZFS boot environments, which are bootable clones of datasets. +.Pp +.Em Boot Environments +allows the system to be upgraded, while preserving the old system environment in a separate ZFS dataset. +.Pp +.Sh COMMANDS +The following commands are supported by +.Nm : +.Bl -tag -width activate +.It Ic activate +.Op Fl t +.Ar +.Pp +Activate the given +.Ar beName +as the default boot filesystem. If the +.Op Fl t +flag is given, this takes effect only for the next boot. +.Pp +.It Ic create +.Op Fl r +.Op Fl e Ar nonActiveBe | Fl e Ar beName@snapshot +.Ao Ar beName Ac +.Pp +Creates a new boot environment named +.Ar beName . +If the -e param is specified, the new environment will be cloned from the given +.Ar nonActiveBe | Ar beName@snapshot . +If the +.Op Fl r +flag is given, a recursive boot environment will be made. +.Pp +.It Ic create +.Op Fl r +.Ao Ar beName@snapshot Ac +.Pp +Creates a snapshot of the existing boot environment named +.Ar beName . +If the +.Op Fl r +flag is given, a recursive boot environment will be made. +.Pp +.It Ic destroy +.Op Fl F +.Ao Ar beName | beName@snapshot Ac +.Pp +Destroys the given +.Ar beName +boot environment or +.Ar beName@snapshot +snapshot. +Specifying +.Fl F +will automatically unmount without confirmation. +.Pp +.It Ic jail +.Ao Ar jailID | jailName Ac +.Ao Ar bootenv Ac +.Pp +Creates a jail of the given boot environment. +.Pp +.It Ic list +.Op Fl a +.Op Fl D +.Op Fl H +.Op Fl s +.Pp +Displays all boot environments. +The Active field indicates whether the boot environment is active now (N); active on reboot (R); or both (NR). +.Pp +If +.Fl a +is used, display all datasets. +If +.Fl D +is used, display the full space usage for each boot environment, assuming all other boot environments were destroyed. +The +.Fl H +option is used for scripting. It does not print headers and separate fields by a single tab instead of arbitrary white space. +If +.Fl s +is used, display all snapshots as well. +.Pp +.It Ic mount +.Ao Ar beName Ac +.Op mountpoint +.Pp +Temporarily mount the boot environment. +Mount at the specified +.Ar mountpoint +if provided. +.Pp +.It Ic rename Ao Ar origBeName Ac Ao Ar newBeName Ac +.Pp +Renames the given nonactive +.Ar origBeName +to the given +.Ar newBeName +.Pp +.It Ic unmount +.Op Fl f +.Ao Ar beName Ac +.Pp +Unmount the given boot environment, if it is mounted. +Specifying +.Fl f +will force the unmount if busy. +.Pp +.It Ic unjail +.Ao Ar beName Ac +.Pp +Destroys the jail created from the given boot environment. +.Pp +.El +.Sh EXAMPLES +.Bl -bullet +.It +To fill in with jail upgrade example when behavior is firm. +.Pp +.Sh SEE ALSO +.Xr jail 8 , +.Xr zfs 8 , +.Xr zpool 8 +.Sh HISTORY +.Nm +is based on +.Xr beadm 1 +and was implemented as a project for the 2017 Summer of Code, along with +.Xr libbe 3 . +.Sh AUTHORS +.Bl -bullet +.It +Kyle Kneitinger (kneitinger) +.Ar kyle@kneit.in +.Pp +Creator of +.Nm . +.It +Slawomir Wojciech Wojtczak (vermaden) +.Ar vermaden@interia.pl +.Pp +Creator and maintainer of +.Xr beadm 1 . +.It +Bryan Drewery (bdrewery) +.Ar bryan@shatow.net +.Pp +Wrote the original +.Xr beadm 1 +manual page that this one is derived from. +.El diff --git a/sbin/be/be.c b/sbin/be/be.c new file mode 100644 index 000000000000..a4803b17cd9e --- /dev/null +++ b/sbin/be/be.c @@ -0,0 +1,664 @@ +/* + * be.c + * + * Copyright (c) 2017 Kyle J. Kneitinger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int be_cmd_activate(int argc, char *argv[]); +static int be_cmd_create(int argc, char *argv[]); +static int be_cmd_destroy(int argc, char *argv[]); +static int be_cmd_export(int argc, char *argv[]); +static int be_cmd_import(int argc, char *argv[]); +static int be_cmd_add(int argc, char *argv[]); +static int be_cmd_jail(int argc, char *argv[]); +static int be_cmd_list(int argc, char *argv[]); +static int be_cmd_mount(int argc, char *argv[]); +static int be_cmd_rename(int argc, char *argv[]); +static int be_cmd_unjail(int argc, char *argv[]); +static int be_cmd_unmount(int argc, char *argv[]); + +libbe_handle_t *be; + +static int +usage(bool explicit) +{ + FILE *fp = explicit ? stdout : stderr; + + fprintf(fp, + "usage:\tbe ( -h | -? | subcommand [args...] )\n" + "\tbe activate [-t] beName\n" + "\tbe create [-e nonActiveBe | -e beName@snapshot] beName\n" + "\tbe create beName@snapshot\n" + "\tbe destroy [-F] beName | beName@snapshot⟩\n" + "\tbe export sourceBe\n" + "\tbe import targetBe\n" + "\tbe add (path)*\n" + "\tbe jail bootenv\n" + "\tbe list [-a] [-D] [-H] [-s]\n" + "\tbe mount beName [mountpoint]\n" + "\tbe rename origBeName newBeName\n" + "\tbe { ujail | unjail } ⟨jailID | jailName⟩ bootenv\n" + "\tbe { umount | unmount } [-f] beName\n"); + + return (explicit ? 0 : EX_USAGE); +} + + +/* + * Represents a relationship between the command name and the parser action + * that handles it. + */ +struct command_map_entry { + const char *command; + int (*fn)(int argc, char *argv[]); +}; + +static struct command_map_entry command_map[] = +{ + { "activate", be_cmd_activate }, + { "create", be_cmd_create }, + { "destroy", be_cmd_destroy }, + { "export", be_cmd_export }, + { "import", be_cmd_import }, + { "add", be_cmd_add }, + { "jail", be_cmd_jail }, + { "list", be_cmd_list }, + { "mount", be_cmd_mount }, + { "rename", be_cmd_rename }, + { "unjail", be_cmd_unjail }, + { "unmount", be_cmd_unmount }, +}; + +static int +get_cmd_index(char *cmd, int *index) +{ + int map_size = sizeof(command_map) / sizeof(struct command_map_entry); + + for (int i = 0; i < map_size; ++i) { + if (strcmp(cmd, command_map[i].command) == 0) { + *index = i; + return (0); + } + } + + return (1); +} + + +static int +be_cmd_activate(int argc, char *argv[]) +{ + int err, opt; + bool temp; + char *bootenv; + + temp = false; + while ((opt = getopt(argc, argv, "t")) != -1) { + switch (opt) { + case 't': + temp = true; + break; + default: + fprintf(stderr, "be activate: unknown option '-%c'\n", + optopt); + return (usage(false)); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "be activate: wrong number of arguments\n"); + return (usage(false)); + } + + + /* activate logic goes here */ + if ((err = be_activate(be, argv[0], temp)) != 0) { + // TODO: more specific error msg based on err + printf("did not successfully activate boot environment %s\n", + argv[0]); + } else { + printf("successfully activated boot environment %s\n", argv[0]); + } + + if (temp) { + printf("for next boot\n"); + } + + return (err); +} + + +// TODO: when only one arg is given, and it contains an "@" the this should +// create that snapshot +static int +be_cmd_create(int argc, char *argv[]) +{ + int err, opt; + char *snapname; + char *bootenv; + char *source; + + snapname = NULL; + while ((opt = getopt(argc, argv, "e:")) != -1) { + switch (opt) { + case 'e': + snapname = optarg; + break; + default: + fprintf(stderr, "be create: unknown option '-%c'\n", + optopt); + return (usage(false)); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "be create: wrong number of arguments\n"); + return (usage(false)); + } + + bootenv = *argv; + + + if (snapname != NULL) { + if (strchr(snapname, '@') != NULL) { + err = be_create_from_existing_snap(be, bootenv, + snapname); + } else { + err = be_create_from_existing(be, bootenv, snapname); + } + } else { + if ((snapname = strchr(bootenv, '@')) != NULL) { + *(snapname++) = '\0'; + if ((err = be_snapshot(be, (char *)be_active_path(be), + snapname, true, NULL)) != BE_ERR_SUCCESS) { + fprintf(stderr, "failed to create snapshot\n"); + } + asprintf(&source, "%s@%s", be_active_path(be), snapname); + err = be_create_from_existing_snap(be, bootenv, + source); + return (err); + } else { + err = be_create(be, bootenv); + } + } + + switch (err) { + case BE_ERR_SUCCESS: + break; + default: + if (snapname == NULL) { + fprintf(stderr, + "failed to create bootenv %s\n", bootenv); + } else { + fprintf(stderr, + "failed to create bootenv %s from snapshot %s\n", + bootenv, snapname); + } + } + + return (err); +} + + +static int +be_cmd_export(int argc, char *argv[]) +{ + int opt; + char *bootenv; + + + if (argc == 1) { + fprintf(stderr, "be export: missing boot environment name\n"); + return (usage(false)); + } + + if (argc > 2) { + fprintf(stderr, "be export: extra arguments provided\n"); + return (usage(false)); + } + + bootenv = argv[1]; + + if (isatty(STDOUT_FILENO)) { + fprintf(stderr, "be export: must redirect output\n"); + return (EX_USAGE); + } + + be_export(be, bootenv, STDOUT_FILENO); + + return (0); +} + + +static int +be_cmd_import(int argc, char *argv[]) +{ + char *bootenv; + int err; + + + if (argc == 1) { + fprintf(stderr, "be import: missing boot environment name\n"); + return (usage(false)); + } + + + if (argc > 2) { + fprintf(stderr, "be import: extra arguments provided\n"); + return (usage(false)); + } + + bootenv = argv[1]; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "be import: input can not be from terminal\n"); + return (EX_USAGE); + } + + err = be_import(be, bootenv, STDIN_FILENO); + + return (err); +} + + +static int +be_cmd_add(int argc, char *argv[]) +{ + char *bootenv; + + if (argc < 2) { + fprintf(stderr, "be add: must provide at least one path\n"); + return (usage(false)); + } + + for (int i = 1; i < argc; ++i) { + printf("arg %d: %s\n", i, argv[i]); + // TODO catch err + be_add_child(be, argv[i], true); + } + + return (0); +} + + +static int +be_cmd_destroy(int argc, char *argv[]) +{ + int opt, err; + bool force; + char *target; + + force = false; + while ((opt = getopt(argc, argv, "F")) != -1) { + switch (opt) { + case 'F': + force = true; + break; + default: + fprintf(stderr, "be destroy: unknown option '-%c'\n", + optopt); + return (usage(false)); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "be destroy: wrong number of arguments\n"); + return (usage(false)); + } + + target = argv[0]; + + err = be_destroy(be, target, force); + + return (err); +} + + +static int +be_cmd_jail(int argc, char *argv[]) +{ + char *bootenv; + char mnt_loc[BE_MAXPATHLEN]; + char buf[BE_MAXPATHLEN*2]; + int err, jid; + + //struct jail be_jail = { 0 }; + + if (argc == 1) { + fprintf(stderr, "be jail: missing boot environment name\n"); + return (usage(false)); + } + if (argc > 2) { + fprintf(stderr, "be jail: too many arguments\n"); + return (usage(false)); + } + + bootenv = argv[1]; + + // TODO: if its already mounted, perhaps there should be a flag to + // indicate its okay to proceed?? + if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) { + fprintf(stderr, "could not mount bootenv\n"); + } + + // NOTE: this is not quite functional: + // see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on + // neccesary modifications to correctly boot the jail + + //snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc", mnt_loc, bootenv, "192.168.1.123"); + snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh", mnt_loc, + bootenv, "192.168.1.123"); + system(buf); + + unmount(mnt_loc, 0); + + /* + * be_jail.version = JAIL_API_VERSION; + * be_jail.path = "/tmp/be_mount.hCCk"; + * be_jail.jailname = "sdfs"; + * + * if ((jid = jail(&be_jail)) != -1) { + * printf("jail %d created at %s\n", jid, mnt_loc); + * err = 0; + * } else { + * fprintf(stderr, "unable to create jail. error: %d\n", errno); + * err = errno; + * } + */ + + return (0); +} + + +static int +be_cmd_list(int argc, char *argv[]) +{ + int opt; + bool show_all_datasets, show_space, hide_headers, show_snaps; + char *bootenv; + nvlist_t *props; + + show_all_datasets = show_space = hide_headers = show_snaps = false; + while ((opt = getopt(argc, argv, "aDHs")) != -1) { + switch (opt) { + case 'a': + show_all_datasets = true; + break; + case 'D': + show_space = true; + break; + case 'H': + hide_headers = true; + break; + case 's': + show_space = true; + break; + default: + fprintf(stderr, "be list: unknown option '-%c'\n", + optopt); + return (usage(false)); + } + } + + argc -= optind; + + if (argc != 0) { + fprintf(stderr, "be list: extra argument provided\n"); + return (usage(false)); + } + + //props = be_get_bootenv_props(be); + + return (0); +} + + +static int +be_cmd_mount(int argc, char *argv[]) +{ + int err; + char result_loc[BE_MAXPATHLEN]; + char *bootenv; + char *mountpoint; + + if (argc < 2) { + fprintf(stderr, "be mount: missing argument(s)\n"); + return (usage(false)); + } + + if (argc > 3) { + fprintf(stderr, "be mount: too many arguments\n"); + return (usage(false)); + } + + bootenv = argv[1]; + mountpoint = ((argc == 3) ? argv[2] : NULL); + + + err = be_mount(be, bootenv, mountpoint, 0, result_loc); + + switch (err) { + case BE_ERR_SUCCESS: + printf("successfully mounted %s at %s\n", bootenv, result_loc); + break; + default: + fprintf(stderr, + (argc == 3) ? "failed to mount bootenv %s at %s\n" : + "failed to mount bootenv %s at temporary path %s\n", + bootenv, mountpoint); + } + + return (err); +} + + +static int +be_cmd_rename(int argc, char *argv[]) +{ + char *src; + char *dest; + int err; + + if (argc < 3) { + fprintf(stderr, "be rename: missing argument\n"); + return (usage(false)); + } + + if (argc > 3) { + fprintf(stderr, "be rename: too many arguments\n"); + return (usage(false)); + } + + src = argv[1]; + dest = argv[2]; + + err = be_rename(be, src, dest); + + switch (err) { + case BE_ERR_SUCCESS: + break; + default: + fprintf(stderr, "failed to rename bootenv %s to %s\n", + src, dest); + } + + return (0); +} + + +static int +be_cmd_unjail(int argc, char *argv[]) +{ + int opt; + char *cmd, *target; + bool force; + + /* Store alias used */ + cmd = argv[0]; + + force = false; + while ((opt = getopt(argc, argv, "f")) != -1) { + switch (opt) { + case 'f': + force = true; + break; + default: + fprintf(stderr, "be %s: unknown option '-%c'\n", + cmd, optopt); + return (usage(false)); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "be %s: wrong number of arguments\n", cmd); + return (usage(false)); + } + + target = argv[0]; + + /* unjail logic goes here */ + + return (0); +} + + +static int +be_cmd_unmount(int argc, char *argv[]) +{ + int err, flags, opt; + char *cmd, *bootenv; + + /* Store alias used */ + cmd = argv[0]; + + flags = 0; + while ((opt = getopt(argc, argv, "f")) != -1) { + switch (opt) { + case 'f': + flags |= BE_MNT_FORCE; + break; + default: + fprintf(stderr, "be %s: unknown option '-%c'\n", + cmd, optopt); + return (usage(false)); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "be %s: wrong number of arguments\n", cmd); + return (usage(false)); + } + + bootenv = argv[0]; + + err = be_unmount(be, bootenv, flags); + + switch (err) { + case BE_ERR_SUCCESS: + break; + default: + fprintf(stderr, "failed to unmount bootenv %s\n", bootenv); + } + + return (err); +} + + +int +main(int argc, char *argv[]) +{ + char *command; + int command_index, rc; + + if (argc < 2) { + fprintf(stderr, "missing command\n"); + return (usage(false)); + } + + command = argv[1]; + + /* Handle command aliases */ + if (strcmp(command, "umount") == 0) { + command = "unmount"; + } + + if (strcmp(command, "ujail") == 0) { + command = "unjail"; + } + + if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0)) { + return (usage(true)); + } + + if (get_cmd_index(command, &command_index)) { + fprintf(stderr, "unknown command: %s\n", command); + return (usage(false)); + } + + + if ((be = libbe_init()) == NULL) { + return (-1); + } + + libbe_print_on_error(be, true); + + /* TODO: can be simplified if offset by 2 instead of one */ + rc = command_map[command_index].fn(argc-1, argv+1); + + libbe_close(be); + + + return (rc); +} diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 1ff6ca3ed22b..65c29ef96ac7 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -62,6 +62,7 @@ _LIBRARIES= \ asn1 \ auditd \ avl \ + be \ begemot \ bluetooth \ bsdxml \ @@ -335,6 +336,7 @@ _DP_zfs= md pthread umem util uutil m nvpair avl bsdxml geom nvpair z \ zfs_core _DP_zfs_core= nvpair _DP_zpool= md pthread z nvpair avl umem +_DP_be= zfs nvpair # OFED support .if ${MK_OFED} != "no" @@ -472,6 +474,8 @@ LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a LIBAMUDIR= ${OBJTOP}/usr.sbin/amd/libamu LIBAMU?= ${LIBAMUDIR}/libamu.a +LIBBE?= ${DESTDIR}${LIBDIR}/libbe.a + LIBPMCSTATDIR= ${OBJTOP}/lib/libpmcstat LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat.a From 5952343e02ab1ecabc7241a755241284418ea2ad Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 24 Jul 2018 13:21:44 +0000 Subject: [PATCH 006/322] Rename be(1) to bectl(8); continues to live in /sbin Discussed with: rpokala, allanjude --- sbin/Makefile | 2 +- sbin/{be => bectl}/Makefile | 4 ++-- sbin/{be/be.1 => bectl/bectl.8} | 0 sbin/{be/be.c => bectl/bectl.c} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename sbin/{be => bectl}/Makefile (80%) rename sbin/{be/be.1 => bectl/bectl.8} (100%) rename sbin/{be/be.c => bectl/bectl.c} (100%) diff --git a/sbin/Makefile b/sbin/Makefile index 13c1c0745bcc..4f08a82fe572 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -86,7 +86,7 @@ SUBDIR.${MK_PF}+= pfctl SUBDIR.${MK_PF}+= pflogd SUBDIR.${MK_QUOTAS}+= quotacheck SUBDIR.${MK_ROUTED}+= routed -SUBDIR.${MK_ZFS}+= be +SUBDIR.${MK_ZFS}+= bectl SUBDIR.${MK_ZFS}+= zfsbootcfg SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/be/Makefile b/sbin/bectl/Makefile similarity index 80% rename from sbin/be/Makefile rename to sbin/bectl/Makefile index 3d3cc4e2314b..7226971ffd77 100644 --- a/sbin/be/Makefile +++ b/sbin/bectl/Makefile @@ -1,8 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 -PROG= be +PROG= bectl WARNS?= 1 -MAN= be.1 +MAN= bectl.8 LIBADD+= be LIBADD+= nvpair diff --git a/sbin/be/be.1 b/sbin/bectl/bectl.8 similarity index 100% rename from sbin/be/be.1 rename to sbin/bectl/bectl.8 diff --git a/sbin/be/be.c b/sbin/bectl/bectl.c similarity index 100% rename from sbin/be/be.c rename to sbin/bectl/bectl.c From edbfe3bcb5052670e21e601703f4d5b7c1969acc Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 24 Jul 2018 13:24:30 +0000 Subject: [PATCH 007/322] bectl(8): Chase rename in manpage --- sbin/bectl/bectl.8 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index 2dd7b3d9d633..93464775f85e 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -14,11 +14,11 @@ .\" @(#)be.1 .\" $FreeBSD$ .\" -.Dd June 15, 2017 -.Dt BE 1 +.Dd July 24, 2018 +.Dt BECTL 8 .Os FreeBSD .Sh NAME -.Nm be +.Nm bectl .Nd Utility to manage Boot Environments on ZFS .Sh SYNOPSIS .Nm From 7b6d4f44eaff62cc9d0ddc091e766f954cafc55f Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 24 Jul 2018 13:31:50 +0000 Subject: [PATCH 008/322] src.libnames.mk: Make libbe entry consistent with others Perhaps using libbe.a from "${DESTDIR}${LIBDIR}" might have been the prevailing technique at one point, but nowadays it appears to be to preferred to spell this as "${OBJTOP}/lib" -- make it so. --- share/mk/src.libnames.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 65c29ef96ac7..a6e86764da9e 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -474,7 +474,8 @@ LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a LIBAMUDIR= ${OBJTOP}/usr.sbin/amd/libamu LIBAMU?= ${LIBAMUDIR}/libamu.a -LIBBE?= ${DESTDIR}${LIBDIR}/libbe.a +LIBBEDIR= ${OBJTOP}/lib/libbe +LIBBE?= ${LIBBEDIR}/libbe.a LIBPMCSTATDIR= ${OBJTOP}/lib/libpmcstat LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat.a From 70a11a8eeaf7930b8332eccef91efe34904720a7 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 24 Jul 2018 15:42:23 +0000 Subject: [PATCH 009/322] libbe(3): Add to cddl build, adjust src.libnames.mk as needed --- cddl/lib/Makefile | 3 +++ {lib => cddl/lib}/libbe/Makefile | 6 ++++++ share/mk/src.libnames.mk | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) rename {lib => cddl/lib}/libbe/Makefile (91%) diff --git a/cddl/lib/Makefile b/cddl/lib/Makefile index 69a0e381dfec..2c4085858e8b 100644 --- a/cddl/lib/Makefile +++ b/cddl/lib/Makefile @@ -4,6 +4,7 @@ SUBDIR= ${_drti} \ libavl \ + ${_libbe} \ libctf \ ${_libdtrace} \ libnvpair \ @@ -16,6 +17,7 @@ SUBDIR= ${_drti} \ SUBDIR.${MK_TESTS}+= tests .if ${MK_ZFS} != "no" +_libbe= libbe _libzfs_core= libzfs_core _libzfs= libzfs .if ${MK_LIBTHR} != "no" @@ -28,6 +30,7 @@ _drti= drti _libdtrace= libdtrace .endif +SUBDIR_DEPEND_libbe= libnvpair libzfs SUBDIR_DEPEND_libdtrace= libctf SUBDIR_DEPEND_libzfs_core= libnvpair SUBDIR_DEPEND_libzfs= libavl libnvpair libumem libuutil libzfs_core diff --git a/lib/libbe/Makefile b/cddl/lib/libbe/Makefile similarity index 91% rename from lib/libbe/Makefile rename to cddl/lib/libbe/Makefile index cbb40af821c8..08a2d6f480be 100644 --- a/lib/libbe/Makefile +++ b/cddl/lib/libbe/Makefile @@ -28,6 +28,9 @@ PACKAGE= lib${LIB} LIB= be SHLIB_MAJOR= 1 SHLIB_MINOR= 0 +LIBBE_SRC= ${SRCTOP}/lib/libbe + +.PATH: ${LIBBE_SRC} SRCS= be.c be_access.c be_error.c be_info.c INCS= be.h MAN= libbe.3 @@ -37,7 +40,10 @@ WARNS?= 1 LIBADD+= zfs LIBADD+= nvpair +CFLAGS+= -I${LIBBE_SRC} +CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common +CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index a6e86764da9e..d4bf73ab5dc7 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -474,7 +474,6 @@ LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a LIBAMUDIR= ${OBJTOP}/usr.sbin/amd/libamu LIBAMU?= ${LIBAMUDIR}/libamu.a -LIBBEDIR= ${OBJTOP}/lib/libbe LIBBE?= ${LIBBEDIR}/libbe.a LIBPMCSTATDIR= ${OBJTOP}/lib/libpmcstat @@ -487,6 +486,7 @@ LIBC_NOSSP_PIC?= ${LIBC_NOSSP_PICDIR}/libc_nossp_pic.a # not using a --sysroot or for meta mode bootstrapping when there is no # Makefile.depend. These are sorted by directory. LIBAVLDIR= ${OBJTOP}/cddl/lib/libavl +LIBBEDIR= ${OBJTOP}/cddl/lib/libbe LIBCTFDIR= ${OBJTOP}/cddl/lib/libctf LIBDTRACEDIR= ${OBJTOP}/cddl/lib/libdtrace LIBNVPAIRDIR= ${OBJTOP}/cddl/lib/libnvpair From 268af06d3e621e189ff31cc6c45d3f41d965b370 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 24 Jul 2018 19:55:02 +0000 Subject: [PATCH 010/322] Normalize bectl(8)/libbe(3) Makefiles, remove Makefile copyright/license Approved by: hselaskey --- cddl/lib/libbe/Makefile | 24 ------------------------ sbin/bectl/Makefile | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/cddl/lib/libbe/Makefile b/cddl/lib/libbe/Makefile index 08a2d6f480be..c15e7c9ebcf6 100644 --- a/cddl/lib/libbe/Makefile +++ b/cddl/lib/libbe/Makefile @@ -1,28 +1,4 @@ # $FreeBSD$ -# -# Copyright (c) 2010 Hans Petter Selasky. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# PACKAGE= lib${LIB} LIB= be diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index 7226971ffd77..a97df7241dee 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -1,4 +1,4 @@ -# @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= bectl WARNS?= 1 From 9e004b219ef0162c6ba861ac6b1f11bc6e318bdc Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 01:04:50 +0000 Subject: [PATCH 011/322] bectl.c: Chase rename --- sbin/bectl/bectl.c | 100 ++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index a4803b17cd9e..14ef15a701d9 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -41,18 +41,18 @@ #include #include -static int be_cmd_activate(int argc, char *argv[]); -static int be_cmd_create(int argc, char *argv[]); -static int be_cmd_destroy(int argc, char *argv[]); -static int be_cmd_export(int argc, char *argv[]); -static int be_cmd_import(int argc, char *argv[]); -static int be_cmd_add(int argc, char *argv[]); -static int be_cmd_jail(int argc, char *argv[]); -static int be_cmd_list(int argc, char *argv[]); -static int be_cmd_mount(int argc, char *argv[]); -static int be_cmd_rename(int argc, char *argv[]); -static int be_cmd_unjail(int argc, char *argv[]); -static int be_cmd_unmount(int argc, char *argv[]); +static int bectl_cmd_activate(int argc, char *argv[]); +static int bectl_cmd_create(int argc, char *argv[]); +static int bectl_cmd_destroy(int argc, char *argv[]); +static int bectl_cmd_export(int argc, char *argv[]); +static int bectl_cmd_import(int argc, char *argv[]); +static int bectl_cmd_add(int argc, char *argv[]); +static int bectl_cmd_jail(int argc, char *argv[]); +static int bectl_cmd_list(int argc, char *argv[]); +static int bectl_cmd_mount(int argc, char *argv[]); +static int bectl_cmd_rename(int argc, char *argv[]); +static int bectl_cmd_unjail(int argc, char *argv[]); +static int bectl_cmd_unmount(int argc, char *argv[]); libbe_handle_t *be; @@ -62,20 +62,20 @@ usage(bool explicit) FILE *fp = explicit ? stdout : stderr; fprintf(fp, - "usage:\tbe ( -h | -? | subcommand [args...] )\n" - "\tbe activate [-t] beName\n" - "\tbe create [-e nonActiveBe | -e beName@snapshot] beName\n" - "\tbe create beName@snapshot\n" - "\tbe destroy [-F] beName | beName@snapshot⟩\n" - "\tbe export sourceBe\n" - "\tbe import targetBe\n" - "\tbe add (path)*\n" - "\tbe jail bootenv\n" - "\tbe list [-a] [-D] [-H] [-s]\n" - "\tbe mount beName [mountpoint]\n" - "\tbe rename origBeName newBeName\n" - "\tbe { ujail | unjail } ⟨jailID | jailName⟩ bootenv\n" - "\tbe { umount | unmount } [-f] beName\n"); + "usage:\tbectl ( -h | -? | subcommand [args...] )\n" + "\tbectl activate [-t] beName\n" + "\tbectl create [-e nonActiveBe | -e beName@snapshot] beName\n" + "\tbectl create beName@snapshot\n" + "\tbectl destroy [-F] beName | beName@snapshot⟩\n" + "\tbectl export sourceBe\n" + "\tbectl import targetBe\n" + "\tbectl add (path)*\n" + "\tbectl jail bootenv\n" + "\tbectl list [-a] [-D] [-H] [-s]\n" + "\tbectl mount beName [mountpoint]\n" + "\tbectl rename origBeName newBeName\n" + "\tbectl { ujail | unjail } ⟨jailID | jailName⟩ bootenv\n" + "\tbectl { umount | unmount } [-f] beName\n"); return (explicit ? 0 : EX_USAGE); } @@ -92,18 +92,18 @@ struct command_map_entry { static struct command_map_entry command_map[] = { - { "activate", be_cmd_activate }, - { "create", be_cmd_create }, - { "destroy", be_cmd_destroy }, - { "export", be_cmd_export }, - { "import", be_cmd_import }, - { "add", be_cmd_add }, - { "jail", be_cmd_jail }, - { "list", be_cmd_list }, - { "mount", be_cmd_mount }, - { "rename", be_cmd_rename }, - { "unjail", be_cmd_unjail }, - { "unmount", be_cmd_unmount }, + { "activate", bectl_cmd_activate }, + { "create", bectl_cmd_create }, + { "destroy", bectl_cmd_destroy }, + { "export", bectl_cmd_export }, + { "import", bectl_cmd_import }, + { "add", bectl_cmd_add }, + { "jail", bectl_cmd_jail }, + { "list", bectl_cmd_list }, + { "mount", bectl_cmd_mount }, + { "rename", bectl_cmd_rename }, + { "unjail", bectl_cmd_unjail }, + { "unmount", bectl_cmd_unmount }, }; static int @@ -123,7 +123,7 @@ get_cmd_index(char *cmd, int *index) static int -be_cmd_activate(int argc, char *argv[]) +bectl_cmd_activate(int argc, char *argv[]) { int err, opt; bool temp; @@ -171,7 +171,7 @@ be_cmd_activate(int argc, char *argv[]) // TODO: when only one arg is given, and it contains an "@" the this should // create that snapshot static int -be_cmd_create(int argc, char *argv[]) +bectl_cmd_create(int argc, char *argv[]) { int err, opt; char *snapname; @@ -244,7 +244,7 @@ be_cmd_create(int argc, char *argv[]) static int -be_cmd_export(int argc, char *argv[]) +bectl_cmd_export(int argc, char *argv[]) { int opt; char *bootenv; @@ -274,7 +274,7 @@ be_cmd_export(int argc, char *argv[]) static int -be_cmd_import(int argc, char *argv[]) +bectl_cmd_import(int argc, char *argv[]) { char *bootenv; int err; @@ -305,7 +305,7 @@ be_cmd_import(int argc, char *argv[]) static int -be_cmd_add(int argc, char *argv[]) +bectl_cmd_add(int argc, char *argv[]) { char *bootenv; @@ -325,7 +325,7 @@ be_cmd_add(int argc, char *argv[]) static int -be_cmd_destroy(int argc, char *argv[]) +bectl_cmd_destroy(int argc, char *argv[]) { int opt, err; bool force; @@ -361,7 +361,7 @@ be_cmd_destroy(int argc, char *argv[]) static int -be_cmd_jail(int argc, char *argv[]) +bectl_cmd_jail(int argc, char *argv[]) { char *bootenv; char mnt_loc[BE_MAXPATHLEN]; @@ -417,7 +417,7 @@ be_cmd_jail(int argc, char *argv[]) static int -be_cmd_list(int argc, char *argv[]) +bectl_cmd_list(int argc, char *argv[]) { int opt; bool show_all_datasets, show_space, hide_headers, show_snaps; @@ -460,7 +460,7 @@ be_cmd_list(int argc, char *argv[]) static int -be_cmd_mount(int argc, char *argv[]) +bectl_cmd_mount(int argc, char *argv[]) { int err; char result_loc[BE_MAXPATHLEN]; @@ -499,7 +499,7 @@ be_cmd_mount(int argc, char *argv[]) static int -be_cmd_rename(int argc, char *argv[]) +bectl_cmd_rename(int argc, char *argv[]) { char *src; char *dest; @@ -533,7 +533,7 @@ be_cmd_rename(int argc, char *argv[]) static int -be_cmd_unjail(int argc, char *argv[]) +bectl_cmd_unjail(int argc, char *argv[]) { int opt; char *cmd, *target; @@ -572,7 +572,7 @@ be_cmd_unjail(int argc, char *argv[]) static int -be_cmd_unmount(int argc, char *argv[]) +bectl_cmd_unmount(int argc, char *argv[]) { int err, flags, opt; char *cmd, *bootenv; From c3a34c08e09c24c9cadf309c393e019063e52616 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 03:08:11 +0000 Subject: [PATCH 012/322] libbe(3): Disambiguate 'active' a little bit, add 'bootfs' - Rename 'active' to 'rootfs', which is used in other places to describe the currently booted (or about to be booted) BE. - Add 'bootfs', which indicates the next boot environment to be booted. This is pulled from the BOOTFS zpool property. - Go ahead and keep an open handle to the active zpool. We might need to enumerate datasets, get properties, and set properties (e.g. bootfs) throughout other libbe bits, and a single handle isn't overly expensive. --- lib/libbe/be.c | 112 ++++++++++++++++++++++++-------------------- lib/libbe/be_impl.h | 4 +- lib/libbe/be_info.c | 4 +- 3 files changed, 66 insertions(+), 54 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index f286cc37b11f..d74d98091f3f 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -52,63 +52,82 @@ libbe_init(void) struct stat sb; dev_t root_dev, boot_dev; libbe_handle_t *lbh; - char *pos; - - // TODO: use errno here?? + char *poolname, *pos; + int pnamelen; + lbh = NULL; + poolname = pos = NULL; + pnamelen = 0; /* Verify that /boot and / are mounted on the same filesystem */ - if (stat("/", &sb) != 0) { - return (NULL); - } + /* TODO: use errno here?? */ + if (stat("/", &sb) != 0) + goto err; root_dev = sb.st_dev; - if (stat("/boot", &sb) != 0) { - return (NULL); - } + 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"); - return (NULL); + goto err; } - if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) { - return (NULL); - } + if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) + goto err; - if ((lbh->lzh = libzfs_init()) == NULL) { - free(lbh); - return (NULL); - } - - /* Obtain path to active boot environment */ - if ((kenv(KENV_GET, "zfs_be_active", lbh->active, - BE_MAXPATHLEN)) == -1) { - libzfs_fini(lbh->lzh); - free(lbh); - return (NULL); - } - - /* Remove leading 'zfs:' if present, otherwise use value as-is */ - if ((pos = strrchr(lbh->active, ':')) != NULL) { - strncpy(lbh->active, pos + sizeof(char), BE_MAXPATHLEN); - } + 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) { - libzfs_fini(lbh->lzh); - free(lbh); - return (NULL); - } + if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) + goto err; /* Remove leading 'zfs:' if present, otherwise use value as-is */ - if ((pos = strrchr(lbh->root, ':')) != NULL) { - strncpy(lbh->root, pos + sizeof(char), BE_MAXPATHLEN); - } + if (strcmp(lbh->root, "zfs:") == 0) + strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char), + BE_MAXPATHLEN); + + if ((pos = strchr(lbh->root, '/')) == NULL) + goto err; + + pnamelen = pos - lbh->root; + poolname = malloc(pnamelen + 1); + if (poolname == NULL) + goto err; + + strncpy(poolname, lbh->root, pnamelen); + poolname[pnamelen] = '\0'; + if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) + goto err; + + if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, + BE_MAXPATHLEN, NULL, true) != 0) + goto err; + + /* Obtain path to boot environment rootfs (currently booted) */ + /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ + if ((kenv(KENV_GET, "zfs_be_active", lbh->rootfs, BE_MAXPATHLEN)) == -1) + goto err; + + /* Remove leading 'zfs:' if present, otherwise use value as-is */ + if (strcmp(lbh->rootfs, "zfs:") == 0) + strncpy(lbh->rootfs, strchr(lbh->rootfs, ':') + sizeof(char), + BE_MAXPATHLEN); return (lbh); +err: + if (lbh != NULL) { + if (lbh->active_phandle != NULL) + zpool_close(lbh->active_phandle); + if (lbh->lzh != NULL) + libzfs_fini(lbh->lzh); + free(lbh); + } + free(poolname); + return (NULL); } @@ -118,6 +137,8 @@ libbe_init(void) void libbe_close(libbe_handle_t *lbh) { + if (lbh->active_phandle != NULL) + zpool_close(lbh->active_phandle); libzfs_fini(lbh->lzh); free(lbh); } @@ -148,7 +169,7 @@ be_destroy(libbe_handle_t *lbh, char *name, int options) return (set_error(lbh, BE_ERR_NOENT)); } - if (strcmp(path, lbh->active) == 0) { + if (strcmp(path, lbh->rootfs) == 0) { return (set_error(lbh, BE_ERR_DESTROYACT)); } @@ -802,7 +823,6 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) { char be_path[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; - zpool_handle_t *zph; uint64_t pool_guid; uint64_t vdev_guid; int zfs_fd; @@ -852,17 +872,7 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) return (BE_ERR_SUCCESS); } else { /* Obtain bootenv zpool */ - strncpy(buf, be_path, BE_MAXPATHLEN); - *(strchr(buf, '/')) = '\0'; - - if ((zph = zpool_open(lbh->lzh, buf)) == NULL) { - // TODO: create error for this - return (-1); - } - printf("asdf\n"); - - err = zpool_set_prop(zph, "bootfs", be_path); - zpool_close(zph); + err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); switch (err) { case 0: diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index 8766e6d09918..923b34d50a8a 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -36,8 +36,10 @@ struct libbe_handle { libzfs_handle_t *lzh; + zpool_handle_t *active_phandle; char root[BE_MAXPATHLEN]; - char active[BE_MAXPATHLEN]; + char rootfs[BE_MAXPATHLEN]; + char bootfs[BE_MAXPATHLEN]; be_error_t error; bool print_on_err; }; diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 747155916a95..93b327d6a6b2 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -43,7 +43,7 @@ static int prop_list_builder(prop_data_t *); const char * be_active_name(libbe_handle_t *lbh) { - return (strrchr(lbh->active, '/') + sizeof(char)); + return (strrchr(lbh->rootfs, '/') + sizeof(char)); } @@ -53,7 +53,7 @@ be_active_name(libbe_handle_t *lbh) const char * be_active_path(libbe_handle_t *lbh) { - return (lbh->active); + return (lbh->rootfs); } From ee16b7c9468b74b0ab7c4549423132bfa5febea0 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 03:30:01 +0000 Subject: [PATCH 013/322] libbe(3): Find rootfs instead by enumerating child datasets of BE root This makes us more resilient to a rename of the bootfs, but still wouldn't withstand pool renames or guid renames. More importantly, this allows `bectl create ` work out of the box to create a boot environment based on the currently booted one. --- lib/libbe/be.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index d74d98091f3f..3c8a8c502877 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -41,6 +41,28 @@ #include "be.h" #include "be_impl.h" +/* + * 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) +{ + libbe_handle_t *lbh; + char *mntpoint; + + lbh = (libbe_handle_t *)data; + if (lbh == NULL) + return (1); + + if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) { + strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN); + return (1); + } + + return (0); +} + /* * Initializes the libbe context to operate in the root boot environment * dataset, for example, zroot/ROOT. @@ -48,16 +70,18 @@ libbe_handle_t * libbe_init(void) { - char buf[BE_MAXPATHLEN]; struct stat sb; dev_t root_dev, boot_dev; 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) @@ -109,13 +133,11 @@ libbe_init(void) /* Obtain path to boot environment rootfs (currently booted) */ /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ - if ((kenv(KENV_GET, "zfs_be_active", lbh->rootfs, BE_MAXPATHLEN)) == -1) + if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL) goto err; - /* Remove leading 'zfs:' if present, otherwise use value as-is */ - if (strcmp(lbh->rootfs, "zfs:") == 0) - strncpy(lbh->rootfs, strchr(lbh->rootfs, ':') + sizeof(char), - BE_MAXPATHLEN); + zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); + zfs_close(rootds); return (lbh); err: @@ -126,6 +148,8 @@ libbe_init(void) libzfs_fini(lbh->lzh); free(lbh); } + if (rootds != NULL) + zfs_close(rootds); free(poolname); return (NULL); } From bfe0869c7f7f74f64fb0218f773e0d04be55de47 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 03:50:01 +0000 Subject: [PATCH 014/322] libbe(3): make style consistent with what I'll use going forward --- lib/libbe/be.c | 294 +++++++++++++++++------------------------- lib/libbe/be_access.c | 46 +++---- lib/libbe/be_error.c | 9 +- lib/libbe/be_info.c | 89 ++++++------- 4 files changed, 179 insertions(+), 259 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 3c8a8c502877..c4e31b3b9a3e 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -161,6 +161,7 @@ libbe_init(void) void libbe_close(libbe_handle_t *lbh) { + if (lbh->active_phandle != NULL) zpool_close(lbh->active_phandle); libzfs_fini(lbh->lzh); @@ -179,30 +180,27 @@ be_destroy(libbe_handle_t *lbh, char *name, int options) { zfs_handle_t *fs; char path[BE_MAXPATHLEN]; - char *p = path; - int mounted; - int force = options & BE_DESTROY_FORCE; + char *p; + int err, force, mounted; - int err = BE_ERR_SUCCESS; + p = path; + force = options & BE_DESTROY_FORCE; + err = BE_ERR_SUCCESS; be_root_concat(lbh, name, path); - printf("path: %s\n", path); if (strchr(name, '@') == NULL) { - if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) { + 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) return (set_error(lbh, BE_ERR_DESTROYACT)); - } fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); } else { - if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) { + 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); } @@ -212,19 +210,17 @@ be_destroy(libbe_handle_t *lbh, char *name, int options) /* Check if mounted, unmount if force is specified */ if (mounted = zfs_is_mounted(fs, NULL)) { - if (force) { + if (force) zfs_unmount(fs, NULL, 0); - } else { + else return (set_error(lbh, BE_ERR_DESTROYMNT)); - } } - // TODO: convert this to use zfs_iter_children first for deep bes - // XXX note: errno 16 (device busy) occurs when chilren are present - if ((err = zfs_destroy(fs, false)) != 0) { + /* XXX TODO: convert this to use zfs_iter_children first for deep BEs */ + /* XXX Note: errno 16 (device busy) occurs when chilren are present */ + if ((err = zfs_destroy(fs, false)) != 0) fprintf(stderr, "delete failed errno: %d\n", errno); - } return (err); } @@ -240,25 +236,22 @@ be_snapshot(libbe_handle_t *lbh, char *source, char *snap_name, be_root_concat(lbh, source, buf); - if (!be_exists(lbh, buf)) { + if (!be_exists(lbh, buf)) return (BE_ERR_NOENT); - } if (snap_name != NULL) { strcat(buf, "@"); strcat(buf, snap_name); - if (result != NULL) { + 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) { + if (result != NULL) strcpy(result, strrchr(buf, '/') + 1); - } } if (err = zfs_snapshot(lbh->lzh, buf, recursive, NULL) != 0) { @@ -267,7 +260,7 @@ be_snapshot(libbe_handle_t *lbh, char *source, char *snap_name, return (set_error(lbh, BE_ERR_INVALIDNAME)); default: - // TODO: elaborate return codes + /* XXX TODO: elaborate return codes */ return (set_error(lbh, BE_ERR_UNKNOWN)); } } @@ -294,11 +287,12 @@ static int be_deep_clone_prop(int prop, void *cb) { int err; - struct libbe_dccb *dccb = cb; + struct libbe_dccb *dccb; zprop_source_t src; char pval[BE_MAXPATHLEN]; char source[BE_MAXPATHLEN]; + dccb = cb; /* Skip some properties we don't want to touch */ switch (prop) { case ZFS_PROP_CANMOUNT: @@ -307,19 +301,17 @@ be_deep_clone_prop(int prop, void *cb) } /* Don't copy readonly properties */ - if (zfs_prop_readonly(prop)) { + if (zfs_prop_readonly(prop)) return (ZPROP_CONT); - } if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, - sizeof(pval), &src, (char *)&source, sizeof(source), false))) { + sizeof(pval), &src, (char *)&source, sizeof(source), false))) /* Just continue if we fail to read a property */ return (ZPROP_CONT); - } + /* Only copy locally defined properties */ - if (src != ZPROP_SRC_LOCAL) { + if (src != ZPROP_SRC_LOCAL) return (ZPROP_CONT); - } nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); @@ -337,30 +329,28 @@ be_deep_clone(zfs_handle_t *ds, void *data) char *dsname; zfs_handle_t *snap_hdl; nvlist_t *props; - struct libbe_deep_clone sdc; - struct libbe_deep_clone *isdc = (struct libbe_deep_clone *)data; + struct libbe_deep_clone *isdc, sdc; struct libbe_dccb dccb; + isdc = (struct libbe_deep_clone *)data; dspath = zfs_get_name(ds); - if ((dsname = strrchr(dspath, '/')) == NULL) { + if ((dsname = strrchr(dspath, '/')) == NULL) return (BE_ERR_UNKNOWN); - } dsname++; - if (isdc->bename == NULL) { + + if (isdc->bename == NULL) snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); - } else { + else snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); - } + snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); - if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) { + if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) return (set_error(isdc->lbh, BE_ERR_EXISTS)); - } if ((snap_hdl = - zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) { + zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); - } nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); @@ -368,9 +358,8 @@ be_deep_clone(zfs_handle_t *ds, void *data) dccb.zhp = ds; dccb.props = props; if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, - ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) { + ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) return (-1); - } if (err = zfs_clone(snap_hdl, be_path, props)) { switch (err) { @@ -409,27 +398,21 @@ be_create_from_existing_snap(libbe_handle_t *lbh, char *name, char *snap) zfs_handle_t *parent_hdl; struct libbe_deep_clone sdc; - if (err = be_validate_name(lbh, name)) { + if (err = be_validate_name(lbh, name)) return (set_error(lbh, err)); - } - - if (err = be_root_concat(lbh, snap, snap_path)) { + if (err = be_root_concat(lbh, snap, snap_path)) return (set_error(lbh, err)); - } - - if (err = be_validate_snap(lbh, snap_path)) { + if (err = be_validate_snap(lbh, snap_path)) return (set_error(lbh, err)); - } - if (err = be_root_concat(lbh, name, be_path)) { + if (err = be_root_concat(lbh, name, be_path)) return (set_error(lbh, err)); - } - if ((bename = strrchr(name, '/')) == NULL) { + if ((bename = strrchr(name, '/')) == NULL) bename = name; - } else { + else bename++; - } + if ((parentname = strdup(snap_path)) == NULL) { err = BE_ERR_UNKNOWN; return (set_error(lbh, err)); @@ -463,9 +446,8 @@ be_create_from_existing(libbe_handle_t *lbh, char *name, char *old) 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))) return (set_error(lbh, err)); - } err = be_create_from_existing_snap(lbh, name, (char *)buf); @@ -487,38 +469,30 @@ be_validate_snap(libbe_handle_t *lbh, char *snap_name) char *mountpoint; int err = BE_ERR_SUCCESS; - if (strlen(snap_name) >= BE_MAXPATHLEN) { + if (strlen(snap_name) >= BE_MAXPATHLEN) return (BE_ERR_PATHLEN); - } if (!zfs_dataset_exists(lbh->lzh, snap_name, - ZFS_TYPE_SNAPSHOT)) { + 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) { + 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) { + 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)) { + if (err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN, + NULL, NULL, 0, 1)) err = BE_ERR_INVORIGIN; - } - if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) { + if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) err = BE_ERR_INVORIGIN; - } zfs_close(zfs_hdl); @@ -544,13 +518,11 @@ be_root_concat(libbe_handle_t *lbh, char *name, char *result) /* Act idempotently; return be name if it is already a full path */ if (strrchr(name, '/') != NULL) { - if (strstr(name, lbh->root) != name) { + if (strstr(name, lbh->root) != name) return (BE_ERR_INVALIDNAME); - } - if (name_len >= BE_MAXPATHLEN) { + if (name_len >= BE_MAXPATHLEN) return (BE_ERR_PATHLEN); - } strncpy(result, name, BE_MAXPATHLEN); return (BE_ERR_SUCCESS); @@ -574,10 +546,8 @@ be_validate_name(libbe_handle_t *lbh, char *name) { for (int i = 0; *name; i++) { char c = *(name++); - if (isalnum(c) || (c == '-') || (c == '_') || - (c == '.')) { + if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) continue; - } return (BE_ERR_INVALIDNAME); } @@ -596,50 +566,42 @@ be_rename(libbe_handle_t *lbh, char *old, char *new) zfs_handle_t *zfs_hdl; int err; - - if (err = be_root_concat(lbh, old, full_old)) { + if (err = be_root_concat(lbh, old, full_old)) return (set_error(lbh, err)); - } - if (err = be_root_concat(lbh, new, full_new)) { + if (err = be_root_concat(lbh, new, full_new)) return (set_error(lbh, err)); - } - if (be_validate_name(lbh, new)) { + if (be_validate_name(lbh, new)) return (BE_ERR_UNKNOWN); - // TODO set and return correct error - } + /* XXX TODO set and return correct error */ - // check if old is active be - if (strcmp(full_new, be_active_path(lbh)) == 0) { + /* Check if old is active BE */ + if (strcmp(full_new, be_active_path(lbh)) == 0) return (BE_ERR_UNKNOWN); - // TODO set and return correct error - } + /* XXX TODO set and return correct error */ - if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) { + if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) return (BE_ERR_UNKNOWN); - // TODO set and return correct error - } + /* XXX TODO set and return correct error */ - if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) { + if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) return (BE_ERR_UNKNOWN); - // TODO set and return correct error - } - - // TODO: what about mounted bes? - // - if mounted error out unless a force flag is set? - + /* XXX TODO set and return correct error */ + /* XXX TODO + * - What about mounted BEs? + * - if mounted error out unless a force flag is set? + */ if ((zfs_hdl = zfs_open(lbh->lzh, full_old, - ZFS_TYPE_FILESYSTEM)) == NULL) { + ZFS_TYPE_FILESYSTEM)) == NULL) return (BE_ERR_UNKNOWN); - // TODO set and return correct error - } + /* XXX TODO set and return correct error */ - // recurse, nounmount, forceunmount + /* recurse, nounmount, forceunmount */ struct renameflags flags = { 0, 0, 0 }; - // TODO: error log on this call + /* XXX TODO: error log on this call */ err = zfs_rename(zfs_hdl, NULL, full_new, flags); zfs_close(zfs_hdl); @@ -656,16 +618,14 @@ be_export(libbe_handle_t *lbh, char *bootenv, int fd) zfs_handle_t *zfs; int err; - if (err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) { - // TODO error handle + if (err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) + /* XXX TODO error handle */ return (-1); - } be_root_concat(lbh, snap_name, buf); - if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) { + if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) return (BE_ERR_ZFSOPEN); - } err = zfs_send_one(zfs, NULL, fd, 0); return (err); @@ -681,32 +641,33 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) zfs_handle_t *zfs; int err, len; - // TODO: this is a very likely name for someone to already have used - if (err = be_root_concat(lbh, "be_import_temp", buf)) { - // TODO error handle + /* + * XXX TODO: this is a very likely name for someone to already have + * used... we should avoid it. + */ + if (err = be_root_concat(lbh, "be_import_temp", buf)) + /* XXX TODO error handle */ return (-1); - } time(&rawtime); len = strlen(buf); strftime(buf + len, BE_MAXPATHLEN - len, "@%F-%T", localtime(&rawtime)); - - // lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { + /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ if (err = lzc_receive(buf, NULL, NULL, false, fd)) { /* TODO: go through libzfs_core's recv_impl and find returned * errors and set appropriate BE_ERR * edit: errors are not in libzfs_core, my assumption is * that they use libzfs errors - * note: 17 is err for dataset already existing */ + * note: 17 is err for dataset already existing + */ return (err); } - if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) { - // TODO correct error + if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) + /* XXX TODO correct error */ return (-1); - } nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); @@ -719,8 +680,7 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) nvlist_free(props); - // TODO: recursively delete be_import_temp dataset - + /* XXX TODO: recursively delete be_import_temp dataset */ return (err); } @@ -736,10 +696,9 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) int err; /* Require absolute paths */ - if (*child_path != '/') { - /* TODO: create appropriate error */ + if (*child_path != '/') + /* XXX TODO: create appropriate error */ return (-1); - } strncpy(active, be_active_path(lbh), BE_MAXPATHLEN); strcpy(buf, active); @@ -761,82 +720,68 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) /* Path does not exist as a descendent of / yet */ int pos = strlen(active); - /* TODO: Verify that resulting str is less than BE_MAXPATHLEN */ + /* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */ strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); - if (stat(child_path, &sb) != 0) { /* Verify that error is ENOENT */ - if (errno != 2) { - /* TODO: create appropriate error */ + if (errno != 2) + /* XXX TODO: create appropriate error */ return (-1); - } nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); nvlist_add_string(props, "mountpoint", child_path); - // create + /* Create */ if (err = - zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) { - /* TODO handle error */ + zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) + /* XXX TODO handle error */ return (-1); - } nvlist_free(props); if ((zfs = - zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) { - /* TODO handle error */ + zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) + /* XXX TODO handle error */ return (-1); - } - // set props - if (err = zfs_prop_set(zfs, "canmount", "noauto")) { + /* Set props */ + if (err = zfs_prop_set(zfs, "canmount", "noauto")) /* TODO handle error */ return (-1); - } } else if (cp_if_exists) { /* Path is already a descendent of / and should be copied */ - - - // TODO + /* XXX TODO ? */ /* * Establish if the existing path is a zfs dataset or just * the subdirectory of one */ - - // TODO: use mktemp + /* XXX TODO: use mktemp */ long int snap_name = random(); snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); - if (err = zfs_snapshot(lbh->lzh, buf, false, NULL)) { - // TODO correct error + if (err = zfs_snapshot(lbh->lzh, buf, false, NULL)) + /* XXX TODO correct error */ return (-1); - } - // clone + /* Clone */ if ((zfs = - zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) { - // TODO correct error + zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) + /* XXX TODO correct error */ return (-1); - } - if (err = zfs_clone(zfs, active, NULL)) { - // TODO correct error + if (err = zfs_clone(zfs, active, NULL)) + /* XXX TODO correct error */ return (-1); - } - - // set props - } else { + /* set props */ + } else /* TODO: error code for exists, but not cp? */ return (-1); - } - return (BE_ERR_SUCCESS); } @@ -855,29 +800,26 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) be_root_concat(lbh, bootenv, be_path); - /* Note: be_exists fails if mountpoint is not / */ - if (!be_exists(lbh, be_path)) { + if (!be_exists(lbh, be_path)) return (BE_ERR_NOENT); - } if (temporary) { - // TODO: give proper attribution to author(s) of zfsbootcfg - // for this snippet + /* + * XXX TODO: give proper attribution to author(s) of zfsbootcfg + * for this snippet. + */ if (kenv(KENV_GET, "vfs.zfs.boot.primary_pool", buf, - sizeof(buf)) <= 0) { + sizeof(buf)) <= 0) return (1); - } pool_guid = strtoumax(buf, NULL, 10); - if (pool_guid == 0) { + if (pool_guid == 0) return (1); - } if (kenv(KENV_GET, "vfs.zfs.boot.primary_vdev", buf, - sizeof(buf)) <= 0) { + sizeof(buf)) <= 0) return (1); - } vdev_guid = strtoumax(buf, NULL, 10); if (vdev_guid == 0) { return (1); @@ -903,7 +845,7 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) return (BE_ERR_SUCCESS); default: - // TODO correct errors + /* XXX TODO correct errors */ return (-1); } } diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index 3fa5b04c76f5..ebd196f99dec 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -43,41 +43,36 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, int mntflags; int err; - if (err = be_root_concat(lbh, bootenv, be)) { + if (err = be_root_concat(lbh, bootenv, be)) return (set_error(lbh, err)); - } - if (!be_exists(lbh, bootenv)) { + if (!be_exists(lbh, bootenv)) return (set_error(lbh, BE_ERR_NOENT)); - } - if (is_mounted(lbh->lzh, be, &path)) { + if (is_mounted(lbh->lzh, be, &path)) return (set_error(lbh, BE_ERR_MOUNTED)); - } - mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; /* Create mountpoint if it is not specified */ if (mountpoint == NULL) { strcpy(mnt_temp, "/tmp/be_mount.XXXX"); - if (mkdtemp(mnt_temp) == NULL) { - // TODO: create error for this + if (mkdtemp(mnt_temp) == NULL) + /* XXX TODO: create error for this */ return (set_error(lbh, BE_ERR_UNKNOWN)); - } } char opt = '\0'; if (err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint, - mntflags, MNTTYPE_ZFS, NULL, 0, &opt, 1)) { - // TODO: zmount returns the nmount error, look into what kind of - // errors we can report from that + mntflags, MNTTYPE_ZFS, NULL, 0, &opt, 1)) + /* + * XXX TODO: zmount returns the nmount error, look into what + * kind of errors we can report from that + */ return (set_error(lbh, BE_ERR_UNKNOWN)); - } - if (result_loc != NULL) { + if (result_loc != NULL) strcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint); - } return (BE_ERR_SUCCESS); } @@ -95,21 +90,18 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) int mntsize; char *mntpath; - if (err = be_root_concat(lbh, bootenv, be)) { + if (err = be_root_concat(lbh, bootenv, be)) return (set_error(lbh, err)); - } - if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { - // TODO correct error + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) + /* XXX TODO correct error */ return (set_error(lbh, BE_ERR_NOMOUNT)); - } mntpath = NULL; for (int i = 0; i < mntsize; ++i) { /* 0x000000de is the type number of zfs */ - if (mntbuf[i].f_type != 0x000000de) { + if (mntbuf[i].f_type != 0x000000de) continue; - } if (strcmp(mntbuf[i].f_mntfromname, be) == 0) { mntpath = mntbuf[i].f_mntonname; @@ -117,16 +109,14 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) } } - if (mntpath == NULL) { + if (mntpath == NULL) return (set_error(lbh, BE_ERR_NOMOUNT)); - } mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; - if (err = unmount(mntpath, mntflags)) { - // TODO correct error + if (err = unmount(mntpath, mntflags)) + /* XXX TODO correct error */ return (set_error(lbh, BE_ERR_NOMOUNT)); - } return (set_error(lbh, BE_ERR_SUCCESS)); } diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c index af5f339dc485..9da17eb492bd 100644 --- a/lib/libbe/be_error.c +++ b/lib/libbe/be_error.c @@ -35,6 +35,7 @@ int libbe_errno(libbe_handle_t *lbh) { + return (lbh->error); } @@ -42,6 +43,7 @@ libbe_errno(libbe_handle_t *lbh) const char * libbe_error_description(libbe_handle_t *lbh) { + switch (lbh->error) { case BE_ERR_INVALIDNAME: return ("invalid boot environment name"); @@ -95,6 +97,7 @@ libbe_error_description(libbe_handle_t *lbh) void libbe_print_on_error(libbe_handle_t *lbh, bool val) { + lbh->print_on_err = val; libzfs_print_on_error(lbh->lzh, val); } @@ -103,13 +106,11 @@ libbe_print_on_error(libbe_handle_t *lbh, bool val) int set_error(libbe_handle_t *lbh, be_error_t err) { - // TODO: should the old error be overwritten or no? + /* XXX TODO: should the old error be overwritten or no? */ lbh->error = err; - - if (lbh->print_on_err && (err != BE_ERR_SUCCESS)) { + if (lbh->print_on_err && (err != BE_ERR_SUCCESS)) fprintf(stderr, "%s\n", libbe_error_description(lbh)); - } return (err); } diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 93b327d6a6b2..1576ca58e635 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -43,6 +43,7 @@ static int prop_list_builder(prop_data_t *); const char * be_active_name(libbe_handle_t *lbh) { + return (strrchr(lbh->rootfs, '/') + sizeof(char)); } @@ -53,6 +54,7 @@ be_active_name(libbe_handle_t *lbh) const char * be_active_path(libbe_handle_t *lbh) { + return (lbh->rootfs); } @@ -63,6 +65,7 @@ be_active_path(libbe_handle_t *lbh) const char * be_root_path(libbe_handle_t *lbh) { + return (lbh->root); } @@ -91,38 +94,33 @@ be_get_bootenv_props(libbe_handle_t *lbh) static int prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) { + char buf[512]; + prop_data_t *data; + libbe_handle_t *lbh; + nvlist_t *props; + const char *dataset, *name; + boolean_t mounted, active, nextboot; + /* - * TODO: + * XXX TODO: * some system for defining constants for the nvlist keys * error checking */ - - char buf[512]; - prop_data_t *data; - boolean_t mounted, active, nextboot; - - data = (prop_data_t *)data_p; - - - nvlist_t *props; - - libbe_handle_t *lbh = data->lbh; - + lbh = data->lbh; nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); - const char *dataset = zfs_get_name(zfs_hdl); + dataset = zfs_get_name(zfs_hdl); nvlist_add_string(props, "dataset", dataset); - const char *name = strrchr(dataset, '/') + 1; + name = strrchr(dataset, '/') + 1; nvlist_add_string(props, "name", name); - mounted = zfs_prop_get_int(zfs_hdl, ZFS_PROP_MOUNTED); nvlist_add_boolean_value(props, "mounted", mounted); - // TODO: NOT CORRECT! Must use is_mounted + /* XXX TODO: NOT CORRECT! Must use is_mounted */ if (mounted) { zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512, NULL, NULL, 0, 1); @@ -130,44 +128,37 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) } if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "origin", buf); - } if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "creation", buf); - } nvlist_add_boolean_value(props, "active", (strcmp(be_active_path(lbh), dataset) == 0)); if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "used", buf); - } if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "usedds", buf); - } if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "usedsnap", buf); - } if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "usedrefreserv", buf); - } if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512, - NULL, NULL, 0, 1)) { + NULL, NULL, 0, 1)) nvlist_add_string(props, "referenced", buf); - } - /* TODO figure out how to read nextboot (set in libzfs_pool.c) */ + /* XXX TODO: Add bootfs info */ nvlist_add_nvlist(data->list, name, props); @@ -177,8 +168,8 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) /* * Updates the properties of each bootenv in the libbe handle - * TODO: rename to be_proplist_update - * TODO: ensure that this is always consistent (run after adds, deletes, + * XXX TODO: rename to be_proplist_update + * XXX TODO: ensure that this is always consistent (run after adds, deletes, * renames,etc */ static int @@ -186,19 +177,15 @@ prop_list_builder(prop_data_t *data) { zfs_handle_t *root_hdl; - if (nvlist_alloc(&(data->list), NV_UNIQUE_NAME, KM_SLEEP) != 0) { - /* TODO: actually handle error */ + if (nvlist_alloc(&(data->list), NV_UNIQUE_NAME, KM_SLEEP) != 0) + /* XXX TODO: actually handle error */ return (1); - } - - if ((root_hdl = - zfs_open(data->lbh->lzh, data->lbh->root, - ZFS_TYPE_FILESYSTEM)) == NULL) { + if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root, + ZFS_TYPE_FILESYSTEM)) == NULL) return (BE_ERR_ZFSOPEN); - } - // TODO: some error checking here + /* XXX TODO: some error checking here */ zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data); zfs_close(root_hdl); @@ -214,17 +201,15 @@ void be_prop_list_free(nvlist_t *be_list) { nvlist_t *prop_list; + nvpair_t *be_pair; - nvpair_t *be_pair = nvlist_next_nvpair(be_list, NULL); - - if (nvpair_value_nvlist(be_pair, &prop_list) == 0) { + be_pair = nvlist_next_nvpair(be_list, NULL); + if (nvpair_value_nvlist(be_pair, &prop_list) == 0) nvlist_free(prop_list); - } while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) { - if (nvpair_value_nvlist(be_pair, &prop_list) == 0) { + if (nvpair_value_nvlist(be_pair, &prop_list) == 0) nvlist_free(prop_list); - } } } @@ -239,7 +224,9 @@ be_exists(libbe_handle_t *lbh, char *be) be_root_concat(lbh, be, buf); - // TODO: check mountpoint prop and see if its /, AND that result with below - // expression + /* + * XXX TODO: check mountpoint prop and see if its /, AND that result + * with below expression. + */ return (zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET)); } From 2c8489579a8fad5e3de1a650f245e9482093dcc6 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 03:57:28 +0000 Subject: [PATCH 015/322] bectl(8): Rename error messages --- sbin/bectl/bectl.c | 51 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 14ef15a701d9..226cff574564 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -136,7 +136,7 @@ bectl_cmd_activate(int argc, char *argv[]) temp = true; break; default: - fprintf(stderr, "be activate: unknown option '-%c'\n", + fprintf(stderr, "bectl activate: unknown option '-%c'\n", optopt); return (usage(false)); } @@ -146,7 +146,7 @@ bectl_cmd_activate(int argc, char *argv[]) argv += optind; if (argc != 1) { - fprintf(stderr, "be activate: wrong number of arguments\n"); + fprintf(stderr, "bectl activate: wrong number of arguments\n"); return (usage(false)); } @@ -185,7 +185,7 @@ bectl_cmd_create(int argc, char *argv[]) snapname = optarg; break; default: - fprintf(stderr, "be create: unknown option '-%c'\n", + fprintf(stderr, "bectl create: unknown option '-%c'\n", optopt); return (usage(false)); } @@ -195,13 +195,12 @@ bectl_cmd_create(int argc, char *argv[]) argv += optind; if (argc != 1) { - fprintf(stderr, "be create: wrong number of arguments\n"); + fprintf(stderr, "bectl create: wrong number of arguments\n"); return (usage(false)); } bootenv = *argv; - if (snapname != NULL) { if (strchr(snapname, '@') != NULL) { err = be_create_from_existing_snap(be, bootenv, @@ -251,19 +250,19 @@ bectl_cmd_export(int argc, char *argv[]) if (argc == 1) { - fprintf(stderr, "be export: missing boot environment name\n"); + fprintf(stderr, "bectl export: missing boot environment name\n"); return (usage(false)); } if (argc > 2) { - fprintf(stderr, "be export: extra arguments provided\n"); + fprintf(stderr, "bectl export: extra arguments provided\n"); return (usage(false)); } bootenv = argv[1]; if (isatty(STDOUT_FILENO)) { - fprintf(stderr, "be export: must redirect output\n"); + fprintf(stderr, "bectl export: must redirect output\n"); return (EX_USAGE); } @@ -281,20 +280,20 @@ bectl_cmd_import(int argc, char *argv[]) if (argc == 1) { - fprintf(stderr, "be import: missing boot environment name\n"); + fprintf(stderr, "bectl import: missing boot environment name\n"); return (usage(false)); } if (argc > 2) { - fprintf(stderr, "be import: extra arguments provided\n"); + fprintf(stderr, "bectl import: extra arguments provided\n"); return (usage(false)); } bootenv = argv[1]; if (isatty(STDIN_FILENO)) { - fprintf(stderr, "be import: input can not be from terminal\n"); + fprintf(stderr, "bectl import: input can not be from terminal\n"); return (EX_USAGE); } @@ -310,7 +309,7 @@ bectl_cmd_add(int argc, char *argv[]) char *bootenv; if (argc < 2) { - fprintf(stderr, "be add: must provide at least one path\n"); + fprintf(stderr, "bectl add: must provide at least one path\n"); return (usage(false)); } @@ -338,7 +337,7 @@ bectl_cmd_destroy(int argc, char *argv[]) force = true; break; default: - fprintf(stderr, "be destroy: unknown option '-%c'\n", + fprintf(stderr, "bectl destroy: unknown option '-%c'\n", optopt); return (usage(false)); } @@ -348,7 +347,7 @@ bectl_cmd_destroy(int argc, char *argv[]) argv += optind; if (argc != 1) { - fprintf(stderr, "be destroy: wrong number of arguments\n"); + fprintf(stderr, "bectl destroy: wrong number of arguments\n"); return (usage(false)); } @@ -371,11 +370,11 @@ bectl_cmd_jail(int argc, char *argv[]) //struct jail be_jail = { 0 }; if (argc == 1) { - fprintf(stderr, "be jail: missing boot environment name\n"); + fprintf(stderr, "bectl jail: missing boot environment name\n"); return (usage(false)); } if (argc > 2) { - fprintf(stderr, "be jail: too many arguments\n"); + fprintf(stderr, "bectl jail: too many arguments\n"); return (usage(false)); } @@ -440,7 +439,7 @@ bectl_cmd_list(int argc, char *argv[]) show_space = true; break; default: - fprintf(stderr, "be list: unknown option '-%c'\n", + fprintf(stderr, "bectl list: unknown option '-%c'\n", optopt); return (usage(false)); } @@ -449,7 +448,7 @@ bectl_cmd_list(int argc, char *argv[]) argc -= optind; if (argc != 0) { - fprintf(stderr, "be list: extra argument provided\n"); + fprintf(stderr, "bectl list: extra argument provided\n"); return (usage(false)); } @@ -468,12 +467,12 @@ bectl_cmd_mount(int argc, char *argv[]) char *mountpoint; if (argc < 2) { - fprintf(stderr, "be mount: missing argument(s)\n"); + fprintf(stderr, "bectl mount: missing argument(s)\n"); return (usage(false)); } if (argc > 3) { - fprintf(stderr, "be mount: too many arguments\n"); + fprintf(stderr, "bectl mount: too many arguments\n"); return (usage(false)); } @@ -506,12 +505,12 @@ bectl_cmd_rename(int argc, char *argv[]) int err; if (argc < 3) { - fprintf(stderr, "be rename: missing argument\n"); + fprintf(stderr, "bectl rename: missing argument\n"); return (usage(false)); } if (argc > 3) { - fprintf(stderr, "be rename: too many arguments\n"); + fprintf(stderr, "bectl rename: too many arguments\n"); return (usage(false)); } @@ -549,7 +548,7 @@ bectl_cmd_unjail(int argc, char *argv[]) force = true; break; default: - fprintf(stderr, "be %s: unknown option '-%c'\n", + fprintf(stderr, "bectl %s: unknown option '-%c'\n", cmd, optopt); return (usage(false)); } @@ -559,7 +558,7 @@ bectl_cmd_unjail(int argc, char *argv[]) argv += optind; if (argc != 1) { - fprintf(stderr, "be %s: wrong number of arguments\n", cmd); + fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); return (usage(false)); } @@ -587,7 +586,7 @@ bectl_cmd_unmount(int argc, char *argv[]) flags |= BE_MNT_FORCE; break; default: - fprintf(stderr, "be %s: unknown option '-%c'\n", + fprintf(stderr, "bectl %s: unknown option '-%c'\n", cmd, optopt); return (usage(false)); } @@ -597,7 +596,7 @@ bectl_cmd_unmount(int argc, char *argv[]) argv += optind; if (argc != 1) { - fprintf(stderr, "be %s: wrong number of arguments\n", cmd); + fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); return (usage(false)); } From 16a10da86ca17f97a87b8ef8ca57344330b5a88d Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 04:06:09 +0000 Subject: [PATCH 016/322] bectl(8): Consistent style --- sbin/bectl/bectl.c | 114 ++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 226cff574564..cc47399b7ac8 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -59,8 +59,9 @@ libbe_handle_t *be; static int usage(bool explicit) { - FILE *fp = explicit ? stdout : stderr; + FILE *fp; + fp = explicit ? stdout : stderr; fprintf(fp, "usage:\tbectl ( -h | -? | subcommand [args...] )\n" "\tbectl activate [-t] beName\n" @@ -109,8 +110,9 @@ static struct command_map_entry command_map[] = static int get_cmd_index(char *cmd, int *index) { - int map_size = sizeof(command_map) / sizeof(struct command_map_entry); + int map_size; + map_size = nitems(command_map); for (int i = 0; i < map_size; ++i) { if (strcmp(cmd, command_map[i].command) == 0) { *index = i; @@ -125,9 +127,9 @@ get_cmd_index(char *cmd, int *index) static int bectl_cmd_activate(int argc, char *argv[]) { + char *bootenv; int err, opt; bool temp; - char *bootenv; temp = false; while ((opt = getopt(argc, argv, "t")) != -1) { @@ -152,31 +154,29 @@ bectl_cmd_activate(int argc, char *argv[]) /* activate logic goes here */ - if ((err = be_activate(be, argv[0], temp)) != 0) { - // TODO: more specific error msg based on err + if ((err = be_activate(be, argv[0], temp)) != 0) + /* XXX TODO: more specific error msg based on err */ printf("did not successfully activate boot environment %s\n", argv[0]); - } else { + else printf("successfully activated boot environment %s\n", argv[0]); - } - if (temp) { + if (temp) printf("for next boot\n"); - } return (err); } -// TODO: when only one arg is given, and it contains an "@" the this should -// create that snapshot +/* + * TODO: when only one arg is given, and it contains an "@" the this should + * create that snapshot + */ static int bectl_cmd_create(int argc, char *argv[]) { + char *bootenv, *snapname, *source; int err, opt; - char *snapname; - char *bootenv; - char *source; snapname = NULL; while ((opt = getopt(argc, argv, "e:")) != -1) { @@ -202,40 +202,36 @@ bectl_cmd_create(int argc, char *argv[]) bootenv = *argv; if (snapname != NULL) { - if (strchr(snapname, '@') != NULL) { + if (strchr(snapname, '@') != NULL) err = be_create_from_existing_snap(be, bootenv, snapname); - } else { + else err = be_create_from_existing(be, bootenv, snapname); - } } else { if ((snapname = strchr(bootenv, '@')) != NULL) { *(snapname++) = '\0'; if ((err = be_snapshot(be, (char *)be_active_path(be), - snapname, true, NULL)) != BE_ERR_SUCCESS) { + snapname, true, NULL)) != BE_ERR_SUCCESS) fprintf(stderr, "failed to create snapshot\n"); - } asprintf(&source, "%s@%s", be_active_path(be), snapname); err = be_create_from_existing_snap(be, bootenv, source); return (err); - } else { + } else err = be_create(be, bootenv); - } } switch (err) { case BE_ERR_SUCCESS: break; default: - if (snapname == NULL) { + if (snapname == NULL) fprintf(stderr, "failed to create bootenv %s\n", bootenv); - } else { + else fprintf(stderr, "failed to create bootenv %s from snapshot %s\n", bootenv, snapname); - } } return (err); @@ -245,9 +241,8 @@ bectl_cmd_create(int argc, char *argv[]) static int bectl_cmd_export(int argc, char *argv[]) { - int opt; char *bootenv; - + int opt; if (argc == 1) { fprintf(stderr, "bectl export: missing boot environment name\n"); @@ -278,13 +273,11 @@ bectl_cmd_import(int argc, char *argv[]) char *bootenv; int err; - if (argc == 1) { fprintf(stderr, "bectl import: missing boot environment name\n"); return (usage(false)); } - if (argc > 2) { fprintf(stderr, "bectl import: extra arguments provided\n"); return (usage(false)); @@ -315,7 +308,7 @@ bectl_cmd_add(int argc, char *argv[]) for (int i = 1; i < argc; ++i) { printf("arg %d: %s\n", i, argv[i]); - // TODO catch err + /* XXX TODO catch err */ be_add_child(be, argv[i], true); } @@ -326,9 +319,9 @@ bectl_cmd_add(int argc, char *argv[]) static int bectl_cmd_destroy(int argc, char *argv[]) { + char *target; int opt, err; bool force; - char *target; force = false; while ((opt = getopt(argc, argv, "F")) != -1) { @@ -367,7 +360,7 @@ bectl_cmd_jail(int argc, char *argv[]) char buf[BE_MAXPATHLEN*2]; int err, jid; - //struct jail be_jail = { 0 }; + /* struct jail be_jail = { 0 }; */ if (argc == 1) { fprintf(stderr, "bectl jail: missing boot environment name\n"); @@ -380,17 +373,23 @@ bectl_cmd_jail(int argc, char *argv[]) bootenv = argv[1]; - // TODO: if its already mounted, perhaps there should be a flag to - // indicate its okay to proceed?? - if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) { + /* + * XXX TODO: if its already mounted, perhaps there should be a flag to + * indicate its okay to proceed?? + */ + if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) fprintf(stderr, "could not mount bootenv\n"); - } - // NOTE: this is not quite functional: - // see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on - // neccesary modifications to correctly boot the jail + /* + * NOTE: this is not quite functional: + * see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on + * neccesary modifications to correctly boot the jail + */ - //snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc", mnt_loc, bootenv, "192.168.1.123"); + /* + * snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc", + * mnt_loc, bootenv, "192.168.1.123"); + */ snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh", mnt_loc, bootenv, "192.168.1.123"); system(buf); @@ -418,10 +417,10 @@ bectl_cmd_jail(int argc, char *argv[]) static int bectl_cmd_list(int argc, char *argv[]) { - int opt; - bool show_all_datasets, show_space, hide_headers, show_snaps; char *bootenv; nvlist_t *props; + int opt; + bool show_all_datasets, show_space, hide_headers, show_snaps; show_all_datasets = show_space = hide_headers = show_snaps = false; while ((opt = getopt(argc, argv, "aDHs")) != -1) { @@ -452,8 +451,7 @@ bectl_cmd_list(int argc, char *argv[]) return (usage(false)); } - //props = be_get_bootenv_props(be); - + /* props = be_get_bootenv_props(be); */ return (0); } @@ -461,10 +459,9 @@ bectl_cmd_list(int argc, char *argv[]) static int bectl_cmd_mount(int argc, char *argv[]) { - int err; char result_loc[BE_MAXPATHLEN]; - char *bootenv; - char *mountpoint; + char *bootenv, *mountpoint; + int err; if (argc < 2) { fprintf(stderr, "bectl mount: missing argument(s)\n"); @@ -479,7 +476,6 @@ bectl_cmd_mount(int argc, char *argv[]) bootenv = argv[1]; mountpoint = ((argc == 3) ? argv[2] : NULL); - err = be_mount(be, bootenv, mountpoint, 0, result_loc); switch (err) { @@ -500,8 +496,7 @@ bectl_cmd_mount(int argc, char *argv[]) static int bectl_cmd_rename(int argc, char *argv[]) { - char *src; - char *dest; + char *dest, *src; int err; if (argc < 3) { @@ -534,8 +529,8 @@ bectl_cmd_rename(int argc, char *argv[]) static int bectl_cmd_unjail(int argc, char *argv[]) { - int opt; char *cmd, *target; + int opt; bool force; /* Store alias used */ @@ -565,7 +560,6 @@ bectl_cmd_unjail(int argc, char *argv[]) target = argv[0]; /* unjail logic goes here */ - return (0); } @@ -573,8 +567,8 @@ bectl_cmd_unjail(int argc, char *argv[]) static int bectl_cmd_unmount(int argc, char *argv[]) { + char *bootenv, *cmd; int err, flags, opt; - char *cmd, *bootenv; /* Store alias used */ cmd = argv[0]; @@ -629,17 +623,14 @@ main(int argc, char *argv[]) command = argv[1]; /* Handle command aliases */ - if (strcmp(command, "umount") == 0) { + if (strcmp(command, "umount") == 0) command = "unmount"; - } - if (strcmp(command, "ujail") == 0) { + if (strcmp(command, "ujail") == 0) command = "unjail"; - } - if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0)) { + if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0)) return (usage(true)); - } if (get_cmd_index(command, &command_index)) { fprintf(stderr, "unknown command: %s\n", command); @@ -647,17 +638,14 @@ main(int argc, char *argv[]) } - if ((be = libbe_init()) == NULL) { + if ((be = libbe_init()) == NULL) return (-1); - } libbe_print_on_error(be, true); - /* TODO: can be simplified if offset by 2 instead of one */ + /* XXX TODO: can be simplified if offset by 2 instead of one */ rc = command_map[command_index].fn(argc-1, argv+1); libbe_close(be); - - return (rc); } From 3682d5e902c1b159611c14dc3ef16c436b18b8a8 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 14:30:47 +0000 Subject: [PATCH 017/322] bectl(8): Start dumping out BE information with `bectl list` For the moment, this is a primitive nvlist dump of what we get back from be_get_bootenv_props as a proof-of-concept and to make sure that we're getting back the kind of information we want to see from list. --- lib/libbe/be.h | 3 ++- lib/libbe/be_info.c | 17 ++++++----------- sbin/bectl/Makefile | 6 ++++++ sbin/bectl/bectl.c | 19 ++++++++++++++++--- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 8a352ad9a642..66ac8c104714 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -29,6 +29,7 @@ #ifndef _LIBBE_H #define _LIBBE_H +#include #include #define BE_MAXPATHLEN 512 @@ -63,7 +64,7 @@ const char *be_active_name(libbe_handle_t *); const char *be_active_path(libbe_handle_t *); const char *be_root_path(libbe_handle_t *); -/* nvlist_t *be_get_bootenv_props(libbe_handle_t *); */ +int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); int be_activate(libbe_handle_t *, char *, bool); diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 1576ca58e635..9219d7a75f98 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -71,18 +71,17 @@ be_root_path(libbe_handle_t *lbh) /* - * Returns an nvlist of the bootenv's properties - * TODO: the nvlist should be passed as a param and ints should return status + * Populates dsnvl with one nvlist per bootenv dataset describing the properties + * of that dataset that we've declared ourselves to care about. */ -nvlist_t * -be_get_bootenv_props(libbe_handle_t *lbh) +int +be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) { prop_data_t data; data.lbh = lbh; - prop_list_builder(&data); - - return (data.list); + data.list = dsnvl; + return (prop_list_builder(&data)); } @@ -177,10 +176,6 @@ prop_list_builder(prop_data_t *data) { zfs_handle_t *root_hdl; - if (nvlist_alloc(&(data->list), NV_UNIQUE_NAME, KM_SLEEP) != 0) - /* XXX TODO: actually handle error */ - return (1); - if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root, ZFS_TYPE_FILESYSTEM)) == NULL) return (BE_ERR_ZFSOPEN); diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index a97df7241dee..6f635a71d9bf 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -7,4 +7,10 @@ MAN= bectl.8 LIBADD+= be LIBADD+= nvpair +CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common +CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris +CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common + +CFLAGS+= -DNEED_SOLARIS_BOOLEAN + .include diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index cc47399b7ac8..d0ef1422051b 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -38,7 +39,6 @@ #include #include -#include #include static int bectl_cmd_activate(int argc, char *argv[]); @@ -417,8 +417,8 @@ bectl_cmd_jail(int argc, char *argv[]) static int bectl_cmd_list(int argc, char *argv[]) { - char *bootenv; nvlist_t *props; + char *bootenv; int opt; bool show_all_datasets, show_space, hide_headers, show_snaps; @@ -451,7 +451,20 @@ bectl_cmd_list(int argc, char *argv[]) return (usage(false)); } - /* props = be_get_bootenv_props(be); */ + + if (nvlist_alloc(&props, NV_UNIQUE_NAME, M_WAITOK) != 0) { + fprintf(stderr, "bectl list: failed to allocate prop nvlist\n"); + return (1); + } + if (be_get_bootenv_props(be, props) != 0) { + /* XXX TODO: Real errors */ + fprintf(stderr, "bectl list: failed to fetch boot environments\n"); + return (1); + } + + dump_nvlist(props, 0); + nvlist_free(props); + return (0); } From ff8676cc4e02c6e138f2f3b68a6903dc418e5cc4 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 14:45:00 +0000 Subject: [PATCH 018/322] libbe(3): Add nextboot flag to returned BE information --- lib/libbe/be.c | 3 +++ lib/libbe/be.h | 2 ++ lib/libbe/be_info.c | 24 +++++++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index c4e31b3b9a3e..934cdaf21f9f 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -138,6 +138,9 @@ libbe_init(void) zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); zfs_close(rootds); + rootds = NULL; + if (lbh->rootfs == NULL) + goto err; return (lbh); err: diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 66ac8c104714..2727c1cdfeee 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -62,6 +62,8 @@ void libbe_close(libbe_handle_t *); /* Bootenv information functions: be_info.c */ const char *be_active_name(libbe_handle_t *); const char *be_active_path(libbe_handle_t *); +const char *be_nextboot_name(libbe_handle_t *); +const char *be_nextboot_path(libbe_handle_t *); const char *be_root_path(libbe_handle_t *); int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 9219d7a75f98..6a7e445a5d56 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -58,6 +58,27 @@ be_active_path(libbe_handle_t *lbh) return (lbh->rootfs); } +/* + * Returns the name of the next active boot environment + */ +const char * +be_nextboot_name(libbe_handle_t *lbh) +{ + + return (strrchr(lbh->bootfs, '/') + sizeof(char)); +} + + +/* + * Returns full path of the active boot environment + */ +const char * +be_nextboot_path(libbe_handle_t *lbh) +{ + + return (lbh->bootfs); +} + /* * Returns the path of the boot environment root dataset @@ -157,7 +178,8 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) NULL, NULL, 0, 1)) nvlist_add_string(props, "referenced", buf); - /* XXX TODO: Add bootfs info */ + nvlist_add_boolean_value(props, "nextboot", + (strcmp(be_nextboot_path(lbh), dataset) == 0)); nvlist_add_nvlist(data->list, name, props); From b29bf2f84ea838b1b7dad4e80858b637395930ae Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 15:14:35 +0000 Subject: [PATCH 019/322] libbe(3)/be(8): Drop WARNS overrides, fix all fallout Based on the idea that we shouldn't have all-new library and utility going into base that need WARNS=1... - Decent amount of constification - Lots of parentheses - Minor other nits --- cddl/lib/libbe/Makefile | 2 -- lib/libbe/be.c | 64 ++++++++++++++++++++--------------------- lib/libbe/be.h | 13 +++++---- lib/libbe/be_access.c | 11 ++++--- lib/libbe/be_impl.h | 6 ++-- lib/libbe/be_info.c | 2 +- lib/libbe/libbe.3 | 10 +++---- sbin/bectl/Makefile | 2 -- sbin/bectl/bectl.c | 14 ++++----- 9 files changed, 57 insertions(+), 67 deletions(-) diff --git a/cddl/lib/libbe/Makefile b/cddl/lib/libbe/Makefile index c15e7c9ebcf6..c71d59eaa334 100644 --- a/cddl/lib/libbe/Makefile +++ b/cddl/lib/libbe/Makefile @@ -11,8 +11,6 @@ SRCS= be.c be_access.c be_error.c be_info.c INCS= be.h MAN= libbe.3 -WARNS?= 1 - LIBADD+= zfs LIBADD+= nvpair diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 934cdaf21f9f..48bb36c2beea 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -139,7 +139,7 @@ libbe_init(void) zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); zfs_close(rootds); rootds = NULL; - if (lbh->rootfs == NULL) + if (*lbh->rootfs == '\0') goto err; return (lbh); @@ -212,7 +212,7 @@ be_destroy(libbe_handle_t *lbh, char *name, int options) return (set_error(lbh, BE_ERR_ZFSOPEN)); /* Check if mounted, unmount if force is specified */ - if (mounted = zfs_is_mounted(fs, NULL)) { + if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { if (force) zfs_unmount(fs, NULL, 0); else @@ -230,7 +230,7 @@ be_destroy(libbe_handle_t *lbh, char *name, int options) int -be_snapshot(libbe_handle_t *lbh, char *source, char *snap_name, +be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, bool recursive, char *result) { char buf[BE_MAXPATHLEN]; @@ -257,7 +257,7 @@ be_snapshot(libbe_handle_t *lbh, char *source, char *snap_name, strcpy(result, strrchr(buf, '/') + 1); } - if (err = zfs_snapshot(lbh->lzh, buf, recursive, NULL) != 0) { + if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { switch (err) { case EZFS_INVALIDNAME: return (set_error(lbh, BE_ERR_INVALIDNAME)); @@ -280,7 +280,7 @@ be_create(libbe_handle_t *lbh, char *name) { int err; - err = be_create_from_existing(lbh, name, (char *)be_active_path(lbh)); + err = be_create_from_existing(lbh, name, be_active_path(lbh)); return (set_error(lbh, err)); } @@ -327,7 +327,6 @@ be_deep_clone(zfs_handle_t *ds, void *data) int err; char be_path[BE_MAXPATHLEN]; char snap_path[BE_MAXPATHLEN]; - char mp[BE_MAXPATHLEN]; const char *dspath; char *dsname; zfs_handle_t *snap_hdl; @@ -364,7 +363,7 @@ be_deep_clone(zfs_handle_t *ds, void *data) ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) return (-1); - if (err = zfs_clone(snap_hdl, be_path, props)) { + if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) { switch (err) { case EZFS_SUCCESS: err = BE_ERR_SUCCESS; @@ -392,23 +391,25 @@ be_deep_clone(zfs_handle_t *ds, void *data) * Create the boot environment from pre-existing snapshot */ int -be_create_from_existing_snap(libbe_handle_t *lbh, char *name, char *snap) +be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, + const char *snap) { int err; char be_path[BE_MAXPATHLEN]; char snap_path[BE_MAXPATHLEN]; - char *parentname, *bename, *snapname; + const char *bename; + char *parentname, *snapname; zfs_handle_t *parent_hdl; struct libbe_deep_clone sdc; - if (err = be_validate_name(lbh, name)) + if ((err = be_validate_name(lbh, name)) != 0) return (set_error(lbh, err)); - if (err = be_root_concat(lbh, snap, snap_path)) + if ((err = be_root_concat(lbh, snap, snap_path)) != 0) return (set_error(lbh, err)); - if (err = be_validate_snap(lbh, snap_path)) + if ((err = be_validate_snap(lbh, snap_path)) != 0) return (set_error(lbh, err)); - if (err = be_root_concat(lbh, name, be_path)) + if ((err = be_root_concat(lbh, name, be_path)) != 0) return (set_error(lbh, err)); if ((bename = strrchr(name, '/')) == NULL) @@ -444,7 +445,7 @@ be_create_from_existing_snap(libbe_handle_t *lbh, char *name, char *snap) * Create a boot environment from an existing boot environment */ int -be_create_from_existing(libbe_handle_t *lbh, char *name, char *old) +be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) { int err; char buf[BE_MAXPATHLEN]; @@ -464,12 +465,11 @@ be_create_from_existing(libbe_handle_t *lbh, char *name, char *old) * failure. Does not set the internal library error state. */ int -be_validate_snap(libbe_handle_t *lbh, char *snap_name) +be_validate_snap(libbe_handle_t *lbh, const char *snap_name) { zfs_handle_t *zfs_hdl; char buf[BE_MAXPATHLEN]; char *delim_pos; - char *mountpoint; int err = BE_ERR_SUCCESS; if (strlen(snap_name) >= BE_MAXPATHLEN) @@ -490,8 +490,8 @@ be_validate_snap(libbe_handle_t *lbh, char *snap_name) 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)) + 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)) @@ -512,7 +512,7 @@ be_validate_snap(libbe_handle_t *lbh, char *snap_name) * zfs_be_root. Does not set internal library error state. */ int -be_root_concat(libbe_handle_t *lbh, char *name, char *result) +be_root_concat(libbe_handle_t *lbh, const char *name, char *result) { size_t name_len, root_len; @@ -545,7 +545,7 @@ be_root_concat(libbe_handle_t *lbh, char *name, char *result) * Does not set internal library error state. */ int -be_validate_name(libbe_handle_t *lbh, char *name) +be_validate_name(libbe_handle_t *lbh __unused, const char *name) { for (int i = 0; *name; i++) { char c = *(name++); @@ -569,12 +569,12 @@ be_rename(libbe_handle_t *lbh, char *old, char *new) zfs_handle_t *zfs_hdl; int err; - if (err = be_root_concat(lbh, old, full_old)) + if ((err = be_root_concat(lbh, old, full_old)) != 0) return (set_error(lbh, err)); - if (err = be_root_concat(lbh, new, full_new)) + if ((err = be_root_concat(lbh, new, full_new)) != 0) return (set_error(lbh, err)); - if (be_validate_name(lbh, new)) + if (be_validate_name(lbh, new) != 0) return (BE_ERR_UNKNOWN); /* XXX TODO set and return correct error */ @@ -621,7 +621,7 @@ be_export(libbe_handle_t *lbh, char *bootenv, int fd) zfs_handle_t *zfs; int err; - if (err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) + if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) /* XXX TODO error handle */ return (-1); @@ -648,7 +648,7 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) * XXX TODO: this is a very likely name for someone to already have * used... we should avoid it. */ - if (err = be_root_concat(lbh, "be_import_temp", buf)) + if ((err = be_root_concat(lbh, "be_import_temp", buf)) != 0) /* XXX TODO error handle */ return (-1); @@ -658,7 +658,7 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) "@%F-%T", localtime(&rawtime)); /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ - if (err = lzc_receive(buf, NULL, NULL, false, fd)) { + if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { /* TODO: go through libzfs_core's recv_impl and find returned * errors and set appropriate BE_ERR * edit: errors are not in libzfs_core, my assumption is @@ -737,8 +737,8 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) nvlist_add_string(props, "mountpoint", child_path); /* Create */ - if (err = - zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) + if ((err = + zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) /* XXX TODO handle error */ return (-1); nvlist_free(props); @@ -749,7 +749,7 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) return (-1); /* Set props */ - if (err = zfs_prop_set(zfs, "canmount", "noauto")) + if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) /* TODO handle error */ return (-1); } else if (cp_if_exists) { @@ -767,7 +767,7 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); - if (err = zfs_snapshot(lbh->lzh, buf, false, NULL)) + if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) /* XXX TODO correct error */ return (-1); @@ -777,7 +777,7 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) /* XXX TODO correct error */ return (-1); - if (err = zfs_clone(zfs, active, NULL)) + if ((err = zfs_clone(zfs, active, NULL)) != 0) /* XXX TODO correct error */ return (-1); @@ -797,8 +797,6 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) char buf[BE_MAXPATHLEN]; uint64_t pool_guid; uint64_t vdev_guid; - int zfs_fd; - int len; int err; be_root_concat(lbh, bootenv, be_path); diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 2727c1cdfeee..1b3218c206a4 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -67,14 +67,15 @@ const char *be_nextboot_path(libbe_handle_t *); const char *be_root_path(libbe_handle_t *); int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); +void be_prop_list_free(nvlist_t *be_list); int be_activate(libbe_handle_t *, char *, bool); /* Bootenv creation functions */ int be_create(libbe_handle_t *, char *); -int be_create_from_existing(libbe_handle_t *, char *, char *); -int be_create_from_existing_snap(libbe_handle_t *, char *, char *); -int be_snapshot(libbe_handle_t *, char *, char *, bool, char *); +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 *); /* Bootenv manipulation functions */ int be_rename(libbe_handle_t *, char *, char *); @@ -103,9 +104,9 @@ const char *libbe_error_description(libbe_handle_t *); void libbe_print_on_error(libbe_handle_t *, bool); /* Utility Functions */ -int be_root_concat(libbe_handle_t *, char *, char *); -int be_validate_name(libbe_handle_t *, char *); -int be_validate_snap(libbe_handle_t *, char *); +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_export(libbe_handle_t *, char *, int fd); diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index ebd196f99dec..58176a948222 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -38,12 +38,11 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, { char be[BE_MAXPATHLEN]; char mnt_temp[BE_MAXPATHLEN]; - zfs_handle_t *zfs_hdl; char *path; int mntflags; int err; - if (err = be_root_concat(lbh, bootenv, be)) + if ((err = be_root_concat(lbh, bootenv, be)) != 0) return (set_error(lbh, err)); if (!be_exists(lbh, bootenv)) @@ -63,8 +62,8 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, } char opt = '\0'; - if (err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint, - mntflags, MNTTYPE_ZFS, NULL, 0, &opt, 1)) + if ((err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint, + mntflags, __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) /* * XXX TODO: zmount returns the nmount error, look into what * kind of errors we can report from that @@ -90,7 +89,7 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) int mntsize; char *mntpath; - if (err = be_root_concat(lbh, bootenv, be)) + if ((err = be_root_concat(lbh, bootenv, be)) != 0) return (set_error(lbh, err)); if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) @@ -114,7 +113,7 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; - if (err = unmount(mntpath, mntflags)) + if ((err = unmount(mntpath, mntflags)) != 0) /* XXX TODO correct error */ return (set_error(lbh, BE_ERR_NOMOUNT)); diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index 923b34d50a8a..73f5c22e386e 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -46,9 +46,9 @@ struct libbe_handle { struct libbe_deep_clone { libbe_handle_t *lbh; - char *bename; - char *snapname; - char *be_root; + const char *bename; + const char *snapname; + const char *be_root; }; struct libbe_dccb { diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 6a7e445a5d56..644a93510cdf 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -119,7 +119,7 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) libbe_handle_t *lbh; nvlist_t *props; const char *dataset, *name; - boolean_t mounted, active, nextboot; + boolean_t mounted; /* * XXX TODO: diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index 191840b60338..2ddb2b0a0c58 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -30,7 +30,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 28, 2017 +.Dd July 25, 2018 .Dt LIBBE 3 .Os .Sh NAME @@ -76,7 +76,7 @@ of state to be retained, such as errors from previous operations. .Fn be_create "libbe_handle_t *, char *" ; .Pp .Ft int -.Fn be_create_from_existing "libbe_handle_t *, char *, char *" ; +.Fn be_create_from_existing "libbe_handle_t *, const char *, const char *" ; .Pp .Ft int .Fn be_rename "libbe_handle_t *, char *, char *" ; @@ -109,13 +109,13 @@ of state to be retained, such as errors from previous operations. .Fn libbe_print_on_error "libbe_handle_t *, bool" ; .Pp .Ft int -.Fn be_root_concat "libbe_handle_t *, char *, char *" ; +.Fn be_root_concat "libbe_handle_t *, const char *, char *" ; .Pp .Ft int -.Fn be_validate_name "libbe_handle_t *, char *" ; +.Fn be_validate_name "libbe_handle_t *, const char *" ; .Pp .Ft int -.Fn be_validate_snap "libbe_handle_t *, char *" ; +.Fn be_validate_snap "libbe_handle_t *, const char *" ; .Pp .Ft bool .Fn be_exists "libbe_handle_t *, char *" ; diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index 6f635a71d9bf..0a03f67d964e 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -1,7 +1,6 @@ # $FreeBSD$ PROG= bectl -WARNS?= 1 MAN= bectl.8 LIBADD+= be @@ -9,7 +8,6 @@ LIBADD+= nvpair CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris -CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common CFLAGS+= -DNEED_SOLARIS_BOOLEAN diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index d0ef1422051b..98a9121a2cf3 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -54,7 +54,7 @@ static int bectl_cmd_rename(int argc, char *argv[]); static int bectl_cmd_unjail(int argc, char *argv[]); static int bectl_cmd_unmount(int argc, char *argv[]); -libbe_handle_t *be; +static libbe_handle_t *be; static int usage(bool explicit) @@ -108,7 +108,7 @@ static struct command_map_entry command_map[] = }; static int -get_cmd_index(char *cmd, int *index) +get_cmd_index(const char *cmd, int *index) { int map_size; @@ -127,7 +127,6 @@ get_cmd_index(char *cmd, int *index) static int bectl_cmd_activate(int argc, char *argv[]) { - char *bootenv; int err, opt; bool temp; @@ -210,7 +209,7 @@ bectl_cmd_create(int argc, char *argv[]) } else { if ((snapname = strchr(bootenv, '@')) != NULL) { *(snapname++) = '\0'; - if ((err = be_snapshot(be, (char *)be_active_path(be), + if ((err = be_snapshot(be, be_active_path(be), snapname, true, NULL)) != BE_ERR_SUCCESS) fprintf(stderr, "failed to create snapshot\n"); asprintf(&source, "%s@%s", be_active_path(be), snapname); @@ -242,7 +241,6 @@ static int bectl_cmd_export(int argc, char *argv[]) { char *bootenv; - int opt; if (argc == 1) { fprintf(stderr, "bectl export: missing boot environment name\n"); @@ -299,7 +297,6 @@ bectl_cmd_import(int argc, char *argv[]) static int bectl_cmd_add(int argc, char *argv[]) { - char *bootenv; if (argc < 2) { fprintf(stderr, "bectl add: must provide at least one path\n"); @@ -358,7 +355,7 @@ bectl_cmd_jail(int argc, char *argv[]) char *bootenv; char mnt_loc[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN*2]; - int err, jid; + int err; /* struct jail be_jail = { 0 }; */ @@ -418,7 +415,6 @@ static int bectl_cmd_list(int argc, char *argv[]) { nvlist_t *props; - char *bootenv; int opt; bool show_all_datasets, show_space, hide_headers, show_snaps; @@ -625,7 +621,7 @@ bectl_cmd_unmount(int argc, char *argv[]) int main(int argc, char *argv[]) { - char *command; + const char *command; int command_index, rc; if (argc < 2) { From 4831c931dd0f2f854971c62921f29710037a7e7e Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 15:45:42 +0000 Subject: [PATCH 020/322] libbe(3): Use zfs_is_mounted to check mounted and mountpoint --- lib/libbe/be_info.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 644a93510cdf..3f638163a783 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -114,7 +114,7 @@ be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) static int prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) { - char buf[512]; + char buf[512], *mountpoint; prop_data_t *data; libbe_handle_t *lbh; nvlist_t *props; @@ -137,15 +137,11 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) name = strrchr(dataset, '/') + 1; nvlist_add_string(props, "name", name); - mounted = zfs_prop_get_int(zfs_hdl, ZFS_PROP_MOUNTED); + mounted = zfs_is_mounted(zfs_hdl, &mountpoint); nvlist_add_boolean_value(props, "mounted", mounted); - /* XXX TODO: NOT CORRECT! Must use is_mounted */ - if (mounted) { - zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512, - NULL, NULL, 0, 1); - nvlist_add_string(props, "mountpoint", buf); - } + if (mounted) + nvlist_add_string(props, "mountpoint", mountpoint); if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, NULL, NULL, 0, 1)) From 734e362fa1a843128dc1f437ef16427084cdf46f Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 25 Jul 2018 16:00:48 +0000 Subject: [PATCH 021/322] libbe(3)/bectl(8): Provide and use proper alloc/free for property lists --- lib/libbe/be.h | 1 + lib/libbe/be_info.c | 7 +++++++ lib/libbe/libbe.3 | 8 ++++++++ sbin/bectl/bectl.c | 6 +++--- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 1b3218c206a4..f532718b81d6 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -67,6 +67,7 @@ const char *be_nextboot_path(libbe_handle_t *); const char *be_root_path(libbe_handle_t *); int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); +int be_prop_list_alloc(nvlist_t **be_list); void be_prop_list_free(nvlist_t *be_list); int be_activate(libbe_handle_t *, char *, bool); diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 3f638163a783..7cb014086db0 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -207,6 +207,13 @@ prop_list_builder(prop_data_t *data) } +int +be_prop_list_alloc(nvlist_t **be_list) +{ + + return (nvlist_alloc(be_list, NV_UNIQUE_NAME, KM_SLEEP)); +} + /* * frees property list and its children */ diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index 2ddb2b0a0c58..db94dca78467 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -129,6 +129,14 @@ of state to be retained, such as errors from previous operations. .Ft int .Fn be_add_child "libbe_handle_t *, char *, bool" ; .Pp +.Ft int +.Fn be_prop_list_alloc "nvlist_t **" ; +.Pp +.Ft int +.Fn be_get_bootenv_props "libbe_handle_t *, nvlist_t *" ; +.Pp +.Ft void +.Fn be_prop_list_free "nvlist_t *" ; .\" .Ft void .\" .Fn mp_mfree "MINT *mp" ; .\" .Bd -ragged -offset indent diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 98a9121a2cf3..74f7130e33c8 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -418,6 +417,7 @@ bectl_cmd_list(int argc, char *argv[]) int opt; bool show_all_datasets, show_space, hide_headers, show_snaps; + props = NULL; show_all_datasets = show_space = hide_headers = show_snaps = false; while ((opt = getopt(argc, argv, "aDHs")) != -1) { switch (opt) { @@ -448,7 +448,7 @@ bectl_cmd_list(int argc, char *argv[]) } - if (nvlist_alloc(&props, NV_UNIQUE_NAME, M_WAITOK) != 0) { + if (be_prop_list_alloc(&props) != 0) { fprintf(stderr, "bectl list: failed to allocate prop nvlist\n"); return (1); } @@ -459,7 +459,7 @@ bectl_cmd_list(int argc, char *argv[]) } dump_nvlist(props, 0); - nvlist_free(props); + be_prop_list_free(props); return (0); } From 843e39ce7bf6449f5cbb9144774d589574fda3b1 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 26 Jul 2018 03:13:07 +0000 Subject: [PATCH 022/322] libbe(3): Add be_mounted_at to check a mount point At a bare minimum, this function will return 0 if a BE is mounted at the given path or non-zero otherwise. If the optional 'details' nvlist is supplied, it is filled with an nvpair containing just the information about the BE mounted at the path. This nvpair is structured just as it is for be_get_bootenv_props, except limited to just the single mount point. --- lib/libbe/be.h | 1 + lib/libbe/be_access.c | 63 +++++++++++++++++++++++++++++++++++++++++++ lib/libbe/be_impl.h | 9 ++++++- lib/libbe/be_info.c | 12 ++------- lib/libbe/libbe.3 | 3 +++ 5 files changed, 77 insertions(+), 11 deletions(-) diff --git a/lib/libbe/be.h b/lib/libbe/be.h index f532718b81d6..94d7bb353afd 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -98,6 +98,7 @@ typedef enum { int be_mount(libbe_handle_t *, char *, char *, int, char *); int be_unmount(libbe_handle_t *, char *, int); +int be_mounted_at(libbe_handle_t *, const char *path, nvlist_t *); /* Error related functions: be_error.c */ int libbe_errno(libbe_handle_t *); diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index 58176a948222..917bc3d0463d 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -29,6 +29,69 @@ #include "be.h" #include "be_impl.h" +struct be_mountcheck_info { + const char *path; + char *name; +}; + +static int +be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) +{ + struct be_mountcheck_info *info; + char *mountpoint; + + if (data == NULL) + return (1); + info = (struct be_mountcheck_info *)data; + if (!zfs_is_mounted(zfs_hdl, &mountpoint)) + return (0); + if (strcmp(mountpoint, info->path) == 0) { + info->name = strdup(zfs_get_name(zfs_hdl)); + return (1); + } + return (0); +} + +/* + * usage + */ +int +be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) +{ + char be[BE_MAXPATHLEN + 1]; + zfs_handle_t *root_hdl; + struct be_mountcheck_info info; + prop_data_t propinfo; + + bzero(&be, BE_MAXPATHLEN + 1); + if ((root_hdl = zfs_open(lbh->lzh, lbh->root, + ZFS_TYPE_FILESYSTEM)) == NULL) + return (BE_ERR_ZFSOPEN); + + info.path = path; + info.name = NULL; + zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info); + zfs_close(root_hdl); + + if (info.name != NULL) { + if (details != NULL) { + if ((root_hdl = zfs_open(lbh->lzh, lbh->root, + ZFS_TYPE_FILESYSTEM)) == NULL) { + free(info.name); + return (BE_ERR_ZFSOPEN); + } + + propinfo.lbh = lbh; + propinfo.list = details; + prop_list_builder_cb(root_hdl, &propinfo); + zfs_close(root_hdl); + } + free(info.name); + return (0); + } + return (1); +} + /* * usage */ diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index 73f5c22e386e..b8f61e99591b 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -33,7 +33,6 @@ #include "be.h" - struct libbe_handle { libzfs_handle_t *lzh; zpool_handle_t *active_phandle; @@ -56,6 +55,14 @@ struct libbe_dccb { nvlist_t *props; }; +typedef struct prop_data { + nvlist_t *list; + libbe_handle_t *lbh; +} prop_data_t; + +int prop_list_builder_cb(zfs_handle_t *, void *); +int prop_list_builder(prop_data_t *); + int set_error(libbe_handle_t *, be_error_t); #endif /* _LIBBE_IMPL_H */ diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 7cb014086db0..959958304bf4 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -29,14 +29,6 @@ #include "be.h" #include "be_impl.h" -typedef struct prop_data { - nvlist_t *list; - libbe_handle_t *lbh; -} prop_data_t; - -static int prop_list_builder_cb(zfs_handle_t *, void *); -static int prop_list_builder(prop_data_t *); - /* * Returns the name of the active boot environment */ @@ -111,7 +103,7 @@ be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) * the bootenv root, populate an nvlist_t of its relevant properties. * TODO: should any other properties be included? */ -static int +int prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) { char buf[512], *mountpoint; @@ -189,7 +181,7 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) * XXX TODO: ensure that this is always consistent (run after adds, deletes, * renames,etc */ -static int +int prop_list_builder(prop_data_t *data) { zfs_handle_t *root_hdl; diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index db94dca78467..9689ffc00a21 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -97,6 +97,9 @@ of state to be retained, such as errors from previous operations. .Fn be_mount "libbe_handle_t *, char *, char *, int" ; .Pp .Ft int +.Fn be_mounted_at "libbe_handle_t *, const char *, nvlist_t" ; +.Pp +.Ft int .Fn be_unmount "libbe_handle_t *, char *, int" ; .Pp .Ft int From ad765da4e86fa08e52aa23860313c99c582b5008 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 26 Jul 2018 03:14:58 +0000 Subject: [PATCH 023/322] bectl(8): Support unjailing a boot environment The given parameter may either be a jid, jail name, or a BE name. In all cases, the parameter will be resolved to a jid and bectl(8) will sanity-check that there's actually a BE mounted at the requested jail root before invoking jail_remove(2). --- sbin/bectl/Makefile | 1 + sbin/bectl/bectl.8 | 3 +- sbin/bectl/bectl.c | 109 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 87 insertions(+), 26 deletions(-) diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index 0a03f67d964e..ea80d35fd3ec 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -4,6 +4,7 @@ PROG= bectl MAN= bectl.8 LIBADD+= be +LIBADD+= jail LIBADD+= nvpair CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index 93464775f85e..23843fd85cfc 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -175,8 +175,7 @@ Specifying .Fl f will force the unmount if busy. .Pp -.It Ic unjail -.Ao Ar beName Ac +.It Ic unjail Ao Ar jailID | jailName | beName Ac .Pp Destroys the jail created from the given boot environment. .Pp diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 74f7130e33c8..3662499b9416 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,8 @@ static int bectl_cmd_jail(int argc, char *argv[]); static int bectl_cmd_list(int argc, char *argv[]); static int bectl_cmd_mount(int argc, char *argv[]); static int bectl_cmd_rename(int argc, char *argv[]); +static int bectl_search_jail_paths(const char *mnt); +static int bectl_locate_jail(const char *ident); static int bectl_cmd_unjail(int argc, char *argv[]); static int bectl_cmd_unmount(int argc, char *argv[]); @@ -74,7 +77,7 @@ usage(bool explicit) "\tbectl list [-a] [-D] [-H] [-s]\n" "\tbectl mount beName [mountpoint]\n" "\tbectl rename origBeName newBeName\n" - "\tbectl { ujail | unjail } ⟨jailID | jailName⟩ bootenv\n" + "\tbectl { ujail | unjail } ⟨jailID | jailName | bootenv)\n" "\tbectl { umount | unmount } [-f] beName\n"); return (explicit ? 0 : EX_USAGE); @@ -447,7 +450,6 @@ bectl_cmd_list(int argc, char *argv[]) return (usage(false)); } - if (be_prop_list_alloc(&props) != 0) { fprintf(stderr, "bectl list: failed to allocate prop nvlist\n"); return (1); @@ -534,41 +536,100 @@ bectl_cmd_rename(int argc, char *argv[]) return (0); } +static int +bectl_search_jail_paths(const char *mnt) +{ + char jailpath[MAXPATHLEN + 1]; + int jid; + + jid = 0; + (void)mnt; + while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath, + NULL)) != -1) { + if (strcmp(jailpath, mnt) == 0) + return (jid); + } + + return (-1); +} + +/* + * Locate a jail based on an arbitrary identifier. This may be either a name, + * a jid, or a BE name. Returns the jid or -1 on failure. + */ +static int +bectl_locate_jail(const char *ident) +{ + nvlist_t *belist, *props; + char *mnt; + int jid; + + /* Try the easy-match first */ + jid = jail_getid(ident); + if (jid != -1) + return (jid); + + /* Attempt to try it as a BE name, first */ + if (be_prop_list_alloc(&belist) != 0) + return (-1); + + if (be_get_bootenv_props(be, belist) != 0) + return (-1); + + if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { + /* We'll attempt to resolve the jid by way of mountpoint */ + if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) { + jid = bectl_search_jail_paths(mnt); + be_prop_list_free(belist); + return (jid); + } + + be_prop_list_free(belist); + } + + return (-1); +} static int bectl_cmd_unjail(int argc, char *argv[]) { - char *cmd, *target; - int opt; - bool force; + char path[MAXPATHLEN + 1]; + char *cmd, *name, *target; + int jid; /* Store alias used */ cmd = argv[0]; - force = false; - while ((opt = getopt(argc, argv, "f")) != -1) { - switch (opt) { - case 'f': - force = true; - break; - default: - fprintf(stderr, "bectl %s: unknown option '-%c'\n", - cmd, optopt); - return (usage(false)); - } - } - - argc -= optind; - argv += optind; - - if (argc != 1) { + if (argc != 2) { fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); return (usage(false)); } - target = argv[0]; + target = argv[1]; + + /* Locate the jail */ + if ((jid = bectl_locate_jail(target)) == -1) { + fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, target); + return (1); + } + + bzero(&path, MAXPATHLEN + 1); + name = jail_getname(jid); + if (jail_getv(0, "name", name, "path", path, NULL) != jid) { + free(name); + fprintf(stderr, "bectl %s: failed to get path for jail requested by '%s'\n", cmd, target); + return (1); + } + + free(name); + + if (be_mounted_at(be, path, NULL) != 0) { + fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", cmd, target); + return (1); + } + + jail_remove(jid); - /* unjail logic goes here */ return (0); } From a2d93d3c98723fa193faf55303d991141525bda0 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 26 Jul 2018 04:07:36 +0000 Subject: [PATCH 024/322] bectl(8): Redo jail using jail(3) API The jail is created with allow.mount, allow.mount.devfs, and enforce_statfs=1. Upon creation, we immediately attach, chdir to "/", and drop the user into a shell inside the jail. The default IP for this is arbitrarily 10.20.30.40. --- sbin/bectl/bectl.c | 54 +++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 3662499b9416..e8a8bce10633 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -356,8 +356,7 @@ bectl_cmd_jail(int argc, char *argv[]) { char *bootenv; char mnt_loc[BE_MAXPATHLEN]; - char buf[BE_MAXPATHLEN*2]; - int err; + int err, jid; /* struct jail be_jail = { 0 }; */ @@ -376,39 +375,30 @@ bectl_cmd_jail(int argc, char *argv[]) * XXX TODO: if its already mounted, perhaps there should be a flag to * indicate its okay to proceed?? */ - if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) + if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) { fprintf(stderr, "could not mount bootenv\n"); + return (1); + } - /* - * NOTE: this is not quite functional: - * see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on - * neccesary modifications to correctly boot the jail - */ - - /* - * snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc", - * mnt_loc, bootenv, "192.168.1.123"); - */ - snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh", mnt_loc, - bootenv, "192.168.1.123"); - system(buf); - - unmount(mnt_loc, 0); - - /* - * be_jail.version = JAIL_API_VERSION; - * be_jail.path = "/tmp/be_mount.hCCk"; - * be_jail.jailname = "sdfs"; - * - * if ((jid = jail(&be_jail)) != -1) { - * printf("jail %d created at %s\n", jid, mnt_loc); - * err = 0; - * } else { - * fprintf(stderr, "unable to create jail. error: %d\n", errno); - * err = errno; - * } - */ + /* XXX TODO: Make the IP/hostname configurable? */ + jid = jail_setv(JAIL_CREATE | JAIL_ATTACH, + "name", bootenv, + "path", mnt_loc, + "host.hostname", bootenv, + "persist", "true", + "ip4.addr", "10.20.30.40", + "allow.mount", "true", + "allow.mount.devfs", "true", + "enforce_statfs", "1", + NULL); + if (jid == -1) { + fprintf(stderr, "unable to create jail. error: %d\n", errno); + return (1); + } + /* We're attached within the jail... good bye! */ + chdir("/"); + execl("/bin/sh", "/bin/sh", NULL); return (0); } From 2e355bd3ffb0a7e067c090a314deb2958081e8b2 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 26 Jul 2018 04:12:31 +0000 Subject: [PATCH 025/322] bectl(8): Remove the jail mount point with unjail --- sbin/bectl/bectl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index e8a8bce10633..ddf23287dfcd 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -619,6 +619,7 @@ bectl_cmd_unjail(int argc, char *argv[]) } jail_remove(jid); + unmount(path, 0); return (0); } From 83244ec1ec52a30b49ce8c86a1d12639aba64f79 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 26 Jul 2018 18:56:50 +0000 Subject: [PATCH 026/322] bectl(8): Hack together a more proper `bectl list` Note that the space is currently just the 'used' property of the dataset. --- sbin/bectl/Makefile | 1 + sbin/bectl/bectl.c | 79 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index ea80d35fd3ec..5d1913f03f3a 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -6,6 +6,7 @@ MAN= bectl.8 LIBADD+= be LIBADD+= jail LIBADD+= nvpair +LIBADD+= util CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index ddf23287dfcd..b490d07a425f 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -31,16 +31,24 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#define HEADER_BE "BE" +#define HEADER_ACTIVE "Active" +#define HEADER_MOUNT "Mountpoint" +#define HEADER_SPACE "Space" +#define HEADER_CREATED "Created" + static int bectl_cmd_activate(int argc, char *argv[]); static int bectl_cmd_create(int argc, char *argv[]); static int bectl_cmd_destroy(int argc, char *argv[]); @@ -406,8 +414,14 @@ bectl_cmd_jail(int argc, char *argv[]) static int bectl_cmd_list(int argc, char *argv[]) { - nvlist_t *props; - int opt; +#define BUFSZ 64 + nvpair_t *cur; + nvlist_t *props, *dsprops; + unsigned long long ctimenum, space; + size_t be_maxcol; + int active_colsz, active_colsz_def, be_colsz, mount_colsz, opt, space_colsz; + char buf[BUFSZ], *creation, *mnt, *spacestr; + boolean_t active_now, active_reboot; bool show_all_datasets, show_space, hide_headers, show_snaps; props = NULL; @@ -450,10 +464,69 @@ bectl_cmd_list(int argc, char *argv[]) return (1); } - dump_nvlist(props, 0); + be_maxcol = strlen(HEADER_BE); + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur))); + } + + be_colsz = -be_maxcol; + /* To be made negative after calculating final col sz */ + active_colsz_def = strlen(HEADER_ACTIVE); + mount_colsz = -(int)strlen(HEADER_MOUNT); + space_colsz = -(int)strlen(HEADER_SPACE); + printf("%*s %s %s %s %s\n", be_colsz, HEADER_BE, HEADER_ACTIVE, + HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); + buf[5] = '\0'; + cur = NULL; + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + printf("%*s ", be_colsz, nvpair_name(cur)); + // NR + active_colsz = active_colsz_def; + nvpair_value_nvlist(cur, &dsprops); + if (nvlist_lookup_boolean_value(dsprops, "active", + &active_now) == 0 && active_now) { + printf("N"); + active_colsz--; + } + if (nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot) == 0 && active_reboot) { + printf("R"); + active_colsz--; + } + if (active_colsz == active_colsz_def) { + printf("-"); + active_colsz--; + } + printf("%*s ", -active_colsz, " "); + if (nvlist_lookup_string(dsprops, "mountpoint", &mnt) == 0) + printf("%*s ", mount_colsz, mnt); + else + printf("%*s ", mount_colsz, "-"); + // used + if (nvlist_lookup_string(dsprops, "used", &spacestr) == 0) { + space = strtoull(spacestr, NULL, 10); + humanize_number(buf, 6, space, "", HN_AUTOSCALE, + HN_DECIMAL | HN_NOSPACE | HN_B); + printf("%*s ", space_colsz, buf); + } else + printf("%*s ", space_colsz, "-"); + + if (nvlist_lookup_string(dsprops, "creation", &creation) == 0) { + ctimenum = strtoull(creation, NULL, 10); + strftime(buf, BUFSZ, "%Y-%m-%d %H:%M", + localtime((time_t *)&ctimenum)); + printf("%s", buf); + } + + // creation + printf("\n"); + } be_prop_list_free(props); return (0); +#undef BUFSZ } From 4503a8b6c789dedc15603e262619ab3b9f4f2231 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Thu, 26 Jul 2018 18:57:51 +0000 Subject: [PATCH 027/322] bectl(8): Remove sneaky comment --- sbin/bectl/bectl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index b490d07a425f..f40e7ff9c466 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -520,7 +520,6 @@ bectl_cmd_list(int argc, char *argv[]) printf("%s", buf); } - // creation printf("\n"); } be_prop_list_free(props); From 513044c1814d68d3480c84ac6ff74d2d7fb8d283 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 3 Aug 2018 01:43:51 +0000 Subject: [PATCH 028/322] libbe(3): Fix checking of zfs_prop_get's return value Rather than a boolean_t, it returns an int != 0 for error or 0 for OK. --- lib/libbe/be_info.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 959958304bf4..6e8db75db416 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -136,34 +136,34 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) nvlist_add_string(props, "mountpoint", mountpoint); if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "origin", buf); if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "creation", buf); nvlist_add_boolean_value(props, "active", (strcmp(be_active_path(lbh), dataset) == 0)); if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "used", buf); if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "usedds", buf); if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "usedsnap", buf); if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "usedrefreserv", buf); if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512, - NULL, NULL, 0, 1)) + NULL, NULL, 0, 1) == 0) nvlist_add_string(props, "referenced", buf); nvlist_add_boolean_value(props, "nextboot", From 970fd6543a37e0b3cb92c18744d1cf43660b0b78 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 3 Aug 2018 01:46:46 +0000 Subject: [PATCH 029/322] bectl(8): Move dataset printing out into its own function This may later get reused for printing snapshot and/or origin snapshot information. --- sbin/bectl/bectl.c | 132 ++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index f40e7ff9c466..0001d876af22 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -43,12 +43,19 @@ #include -#define HEADER_BE "BE" +#define HEADER_BE "BE" #define HEADER_ACTIVE "Active" #define HEADER_MOUNT "Mountpoint" #define HEADER_SPACE "Space" #define HEADER_CREATED "Created" +struct printc { + int be_colsz; + int active_colsz_def; + int mount_colsz; + int space_colsz; +}; + static int bectl_cmd_activate(int argc, char *argv[]); static int bectl_cmd_create(int argc, char *argv[]); static int bectl_cmd_destroy(int argc, char *argv[]); @@ -56,6 +63,7 @@ static int bectl_cmd_export(int argc, char *argv[]); static int bectl_cmd_import(int argc, char *argv[]); static int bectl_cmd_add(int argc, char *argv[]); static int bectl_cmd_jail(int argc, char *argv[]); +static void print_dataset(nvpair_t *cur, struct printc *pc); static int bectl_cmd_list(int argc, char *argv[]); static int bectl_cmd_mount(int argc, char *argv[]); static int bectl_cmd_rename(int argc, char *argv[]); @@ -410,18 +418,74 @@ bectl_cmd_jail(int argc, char *argv[]) return (0); } +static void +print_dataset(nvpair_t *cur, struct printc *pc) +{ +#define BUFSZ 64 + char buf[BUFSZ]; + unsigned long long ctimenum, space; + nvlist_t *dsprops; + char *creation, *mnt, *name, *spacestr; + int active_colsz; + boolean_t active_now, active_reboot; + + name = nvpair_name(cur); + /* XXX TODO: Some views show snapshots */ + if (strchr(name, '@') != NULL) + return; + printf("%*s ", pc->be_colsz, name); + + active_colsz = pc->active_colsz_def; + nvpair_value_nvlist(cur, &dsprops); + if (nvlist_lookup_boolean_value(dsprops, "active", + &active_now) == 0 && active_now) { + printf("N"); + active_colsz--; + } + if (nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot) == 0 && active_reboot) { + printf("R"); + active_colsz--; + } + if (active_colsz == pc->active_colsz_def) { + printf("-"); + active_colsz--; + } + printf("%*s ", -active_colsz, " "); + if (nvlist_lookup_string(dsprops, "mountpoint", &mnt) == 0) + printf("%*s ", pc->mount_colsz, mnt); + else + printf("%*s ", pc->mount_colsz, "-"); + + if (nvlist_lookup_string(dsprops, "used", &spacestr) == 0) { + space = strtoull(spacestr, NULL, 10); + + /* Alas, there's more to it,. */ + humanize_number(buf, 6, space, "", HN_AUTOSCALE, + HN_DECIMAL | HN_NOSPACE | HN_B); + printf("%*s ", pc->space_colsz, buf); + } else + printf("%*s ", pc->space_colsz, "-"); + + if (nvlist_lookup_string(dsprops, "creation", &creation) == 0) { + ctimenum = strtoull(creation, NULL, 10); + strftime(buf, BUFSZ, "%Y-%m-%d %H:%M", + localtime((time_t *)&ctimenum)); + printf("%s", buf); + } + + printf("\n"); +#undef BUFSZ +} static int bectl_cmd_list(int argc, char *argv[]) { -#define BUFSZ 64 + struct printc pc; nvpair_t *cur; - nvlist_t *props, *dsprops; - unsigned long long ctimenum, space; + nvlist_t *props; size_t be_maxcol; - int active_colsz, active_colsz_def, be_colsz, mount_colsz, opt, space_colsz; - char buf[BUFSZ], *creation, *mnt, *spacestr; - boolean_t active_now, active_reboot; + int opt; bool show_all_datasets, show_space, hide_headers, show_snaps; props = NULL; @@ -470,62 +534,20 @@ bectl_cmd_list(int argc, char *argv[]) be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur))); } - be_colsz = -be_maxcol; + pc.be_colsz = -be_maxcol; /* To be made negative after calculating final col sz */ - active_colsz_def = strlen(HEADER_ACTIVE); - mount_colsz = -(int)strlen(HEADER_MOUNT); - space_colsz = -(int)strlen(HEADER_SPACE); - printf("%*s %s %s %s %s\n", be_colsz, HEADER_BE, HEADER_ACTIVE, + pc.active_colsz_def = strlen(HEADER_ACTIVE); + pc.mount_colsz = -(int)strlen(HEADER_MOUNT); + pc.space_colsz = -(int)strlen(HEADER_SPACE); + printf("%*s %s %s %s %s\n", pc.be_colsz, HEADER_BE, HEADER_ACTIVE, HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); - buf[5] = '\0'; - cur = NULL; for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; cur = nvlist_next_nvpair(props, cur)) { - printf("%*s ", be_colsz, nvpair_name(cur)); - // NR - active_colsz = active_colsz_def; - nvpair_value_nvlist(cur, &dsprops); - if (nvlist_lookup_boolean_value(dsprops, "active", - &active_now) == 0 && active_now) { - printf("N"); - active_colsz--; - } - if (nvlist_lookup_boolean_value(dsprops, "nextboot", - &active_reboot) == 0 && active_reboot) { - printf("R"); - active_colsz--; - } - if (active_colsz == active_colsz_def) { - printf("-"); - active_colsz--; - } - printf("%*s ", -active_colsz, " "); - if (nvlist_lookup_string(dsprops, "mountpoint", &mnt) == 0) - printf("%*s ", mount_colsz, mnt); - else - printf("%*s ", mount_colsz, "-"); - // used - if (nvlist_lookup_string(dsprops, "used", &spacestr) == 0) { - space = strtoull(spacestr, NULL, 10); - humanize_number(buf, 6, space, "", HN_AUTOSCALE, - HN_DECIMAL | HN_NOSPACE | HN_B); - printf("%*s ", space_colsz, buf); - } else - printf("%*s ", space_colsz, "-"); - - if (nvlist_lookup_string(dsprops, "creation", &creation) == 0) { - ctimenum = strtoull(creation, NULL, 10); - strftime(buf, BUFSZ, "%Y-%m-%d %H:%M", - localtime((time_t *)&ctimenum)); - printf("%s", buf); - } - - printf("\n"); + print_dataset(cur, &pc); } be_prop_list_free(props); return (0); -#undef BUFSZ } From 74c55b3d388e99eefebf99bf3776e87eefd476d9 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 3 Aug 2018 01:51:44 +0000 Subject: [PATCH 030/322] bectl(8): Just use a single pointer for all string props --- sbin/bectl/bectl.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 0001d876af22..16efca2a8f51 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -425,15 +425,15 @@ print_dataset(nvpair_t *cur, struct printc *pc) char buf[BUFSZ]; unsigned long long ctimenum, space; nvlist_t *dsprops; - char *creation, *mnt, *name, *spacestr; + char *propstr; int active_colsz; boolean_t active_now, active_reboot; - name = nvpair_name(cur); + propstr = nvpair_name(cur); /* XXX TODO: Some views show snapshots */ - if (strchr(name, '@') != NULL) + if (strchr(propstr, '@') != NULL) return; - printf("%*s ", pc->be_colsz, name); + printf("%*s ", pc->be_colsz, propstr); active_colsz = pc->active_colsz_def; nvpair_value_nvlist(cur, &dsprops); @@ -452,13 +452,13 @@ print_dataset(nvpair_t *cur, struct printc *pc) active_colsz--; } printf("%*s ", -active_colsz, " "); - if (nvlist_lookup_string(dsprops, "mountpoint", &mnt) == 0) - printf("%*s ", pc->mount_colsz, mnt); + if (nvlist_lookup_string(dsprops, "mountpoint", &propstr) == 0) + printf("%*s ", pc->mount_colsz, propstr); else printf("%*s ", pc->mount_colsz, "-"); - if (nvlist_lookup_string(dsprops, "used", &spacestr) == 0) { - space = strtoull(spacestr, NULL, 10); + if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { + space = strtoull(propstr, NULL, 10); /* Alas, there's more to it,. */ humanize_number(buf, 6, space, "", HN_AUTOSCALE, @@ -467,8 +467,8 @@ print_dataset(nvpair_t *cur, struct printc *pc) } else printf("%*s ", pc->space_colsz, "-"); - if (nvlist_lookup_string(dsprops, "creation", &creation) == 0) { - ctimenum = strtoull(creation, NULL, 10); + if (nvlist_lookup_string(dsprops, "creation", &propstr) == 0) { + ctimenum = strtoull(propstr, NULL, 10); strftime(buf, BUFSZ, "%Y-%m-%d %H:%M", localtime((time_t *)&ctimenum)); printf("%s", buf); From 4146029bb31648f465c87a46dc0bc79e913816ad Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 3 Aug 2018 02:04:57 +0000 Subject: [PATCH 031/322] bectl(8): Take origin snapshot into account when calculating used space This more closely matches the behavior for beadm. The associated libbe(3) API is still getting worked out a little bit. --- lib/libbe/be.h | 1 + lib/libbe/be_access.c | 1 + lib/libbe/be_impl.h | 1 + lib/libbe/be_info.c | 27 +++++++++++++++++++++++++-- lib/libbe/libbe.3 | 3 +++ sbin/bectl/bectl.c | 23 ++++++++++++++++++++++- 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 94d7bb353afd..988baaf16b08 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -67,6 +67,7 @@ const char *be_nextboot_path(libbe_handle_t *); const char *be_root_path(libbe_handle_t *); int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); +int be_get_snapshot_props(libbe_handle_t *, const char *, nvlist_t *); int be_prop_list_alloc(nvlist_t **be_list); void be_prop_list_free(nvlist_t *be_list); diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index 917bc3d0463d..55481e04e14b 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -83,6 +83,7 @@ be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) propinfo.lbh = lbh; propinfo.list = details; + propinfo.single_object = false; prop_list_builder_cb(root_hdl, &propinfo); zfs_close(root_hdl); } diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index b8f61e99591b..89894f4ac026 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -58,6 +58,7 @@ struct libbe_dccb { typedef struct prop_data { nvlist_t *list; libbe_handle_t *lbh; + bool single_object; /* list will contain props directly */ } prop_data_t; int prop_list_builder_cb(zfs_handle_t *, void *); diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 6e8db75db416..c4059ea5d598 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -94,9 +94,28 @@ be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) data.lbh = lbh; data.list = dsnvl; + data.single_object = false; return (prop_list_builder(&data)); } +int +be_get_snapshot_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) +{ + zfs_handle_t *snap_hdl; + prop_data_t data; + int ret; + + data.lbh = lbh; + data.list = props; + data.single_object = true; + if ((snap_hdl = zfs_open(lbh->lzh, name, + ZFS_TYPE_SNAPSHOT)) == NULL) + return (BE_ERR_ZFSOPEN); + + ret = prop_list_builder_cb(snap_hdl, &data); + zfs_close(snap_hdl); + return (ret); +} /* * Internal callback function used by zfs_iter_filesystems. For each dataset in @@ -121,7 +140,10 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) data = (prop_data_t *)data_p; lbh = data->lbh; - nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); + if (data->single_object) + props = data->list; + else + nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); dataset = zfs_get_name(zfs_hdl); nvlist_add_string(props, "dataset", dataset); @@ -169,7 +191,8 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) nvlist_add_boolean_value(props, "nextboot", (strcmp(be_nextboot_path(lbh), dataset) == 0)); - nvlist_add_nvlist(data->list, name, props); + if (!data->single_object) + nvlist_add_nvlist(data->list, name, props); return (0); } diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index 9689ffc00a21..365a1e85dc66 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -138,6 +138,9 @@ of state to be retained, such as errors from previous operations. .Ft int .Fn be_get_bootenv_props "libbe_handle_t *, nvlist_t *" ; .Pp +.Ft int +.Fn be_get_snapshot_props "libbe_handle_t *, const char *, nvlist_t *" ; +.Pp .Ft void .Fn be_prop_list_free "nvlist_t *" ; .\" .Ft void diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 16efca2a8f51..7c0b629e066a 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -424,11 +424,12 @@ print_dataset(nvpair_t *cur, struct printc *pc) #define BUFSZ 64 char buf[BUFSZ]; unsigned long long ctimenum, space; - nvlist_t *dsprops; + nvlist_t *dsprops, *originprops; char *propstr; int active_colsz; boolean_t active_now, active_reboot; + originprops = NULL; propstr = nvpair_name(cur); /* XXX TODO: Some views show snapshots */ if (strchr(propstr, '@') != NULL) @@ -457,9 +458,27 @@ print_dataset(nvpair_t *cur, struct printc *pc) else printf("%*s ", pc->mount_colsz, "-"); + if (nvlist_lookup_string(dsprops, "origin", &propstr) == 0) { + if (be_prop_list_alloc(&originprops) != 0) { + fprintf(stderr, + "bectl list: failed to allocate origin prop nvlist\n"); + return; + } + if (be_get_snapshot_props(be, propstr, originprops) != 0) { + /* XXX TODO: Real errors */ + fprintf(stderr, + "bectl list: failed to fetch origin properties\n"); + return; + } + } + if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { space = strtoull(propstr, NULL, 10); + if (originprops != NULL && nvlist_lookup_string(originprops, + "used", &propstr) == 0) + space += strtoull(propstr, NULL, 10); + /* Alas, there's more to it,. */ humanize_number(buf, 6, space, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B); @@ -475,6 +494,8 @@ print_dataset(nvpair_t *cur, struct printc *pc) } printf("\n"); + if (originprops != NULL) + be_prop_list_free(originprops); #undef BUFSZ } From 77d5a868a0a7c16bb2a478516bfbb0fd8883b924 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sat, 4 Aug 2018 06:14:54 +0000 Subject: [PATCH 032/322] bectl(8): Add some relevant `bectl list -a` information bectl list -a should show the boot environment, its relevant dataset, and the snapshot it was created from. The header also changes to indicate the rough order in which these things will show. While here, start doing a little bit of spring cleaning and splitting different bits out. --- sbin/bectl/bectl.c | 183 ++++++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 50 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 7c0b629e066a..59174d8ad5fa 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -44,16 +44,26 @@ #include #define HEADER_BE "BE" +#define HEADER_BEPLUS "BE/Dataset/Snapshot" #define HEADER_ACTIVE "Active" #define HEADER_MOUNT "Mountpoint" #define HEADER_SPACE "Space" #define HEADER_CREATED "Created" +/* Spaces */ +#define INDENT_INCREMENT 2 + struct printc { - int be_colsz; - int active_colsz_def; - int mount_colsz; - int space_colsz; + int active_colsz_def; + int be_colsz; + int current_indent; + int mount_colsz; + int space_colsz; + bool final_be; + bool hide_headers; + bool show_all_datasets; + bool show_snaps; + bool show_space; }; static int bectl_cmd_activate(int argc, char *argv[]); @@ -63,7 +73,9 @@ static int bectl_cmd_export(int argc, char *argv[]); static int bectl_cmd_import(int argc, char *argv[]); static int bectl_cmd_add(int argc, char *argv[]); static int bectl_cmd_jail(int argc, char *argv[]); -static void print_dataset(nvpair_t *cur, struct printc *pc); +static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops); +static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc); +static void print_headers(nvlist_t *props, struct printc *pc); static int bectl_cmd_list(int argc, char *argv[]); static int bectl_cmd_mount(int argc, char *argv[]); static int bectl_cmd_rename(int argc, char *argv[]); @@ -418,26 +430,71 @@ bectl_cmd_jail(int argc, char *argv[]) return (0); } +/* + * Given a set of dataset properties (for a BE dataset), populate originprops + * with the origin's properties. + */ +static const char * +get_origin_props(nvlist_t *dsprops, nvlist_t **originprops) +{ + char *propstr; + + if (nvlist_lookup_string(dsprops, "origin", &propstr) == 0) { + if (be_prop_list_alloc(originprops) != 0) { + fprintf(stderr, + "bectl list: failed to allocate origin prop nvlist\n"); + return (NULL); + } + if (be_get_snapshot_props(be, propstr, *originprops) != 0) { + /* XXX TODO: Real errors */ + fprintf(stderr, + "bectl list: failed to fetch origin properties\n"); + return (NULL); + } + + return (propstr); + } + return (NULL); +} + + static void -print_dataset(nvpair_t *cur, struct printc *pc) +print_info(const char *name, nvlist_t *dsprops, struct printc *pc) { #define BUFSZ 64 char buf[BUFSZ]; unsigned long long ctimenum, space; - nvlist_t *dsprops, *originprops; + nvlist_t *originprops; + const char *oname; char *propstr; int active_colsz; boolean_t active_now, active_reboot; originprops = NULL; - propstr = nvpair_name(cur); - /* XXX TODO: Some views show snapshots */ - if (strchr(propstr, '@') != NULL) + printf("%*s%*s ", pc->current_indent, "", + pc->be_colsz + pc->current_indent, name); + + /* Recurse at the base level if we're breaking info down */ + if (pc->current_indent == 0 && (pc->show_all_datasets || + pc->show_snaps)) { + printf("\n"); + if (nvlist_lookup_string(dsprops, "dataset", &propstr) != 0) + /* XXX TODO: Error? */ + return; + pc->current_indent += INDENT_INCREMENT; + print_info(propstr, dsprops, pc); + pc->current_indent += INDENT_INCREMENT; + if ((oname = get_origin_props(dsprops, &originprops)) != NULL) { + print_info(oname, originprops, pc); + nvlist_free(originprops); + } + pc->current_indent = 0; + if (!pc->final_be) + printf("\n"); return; - printf("%*s ", pc->be_colsz, propstr); + } active_colsz = pc->active_colsz_def; - nvpair_value_nvlist(cur, &dsprops); if (nvlist_lookup_boolean_value(dsprops, "active", &active_now) == 0 && active_now) { printf("N"); @@ -458,25 +515,13 @@ print_dataset(nvpair_t *cur, struct printc *pc) else printf("%*s ", pc->mount_colsz, "-"); - if (nvlist_lookup_string(dsprops, "origin", &propstr) == 0) { - if (be_prop_list_alloc(&originprops) != 0) { - fprintf(stderr, - "bectl list: failed to allocate origin prop nvlist\n"); - return; - } - if (be_get_snapshot_props(be, propstr, originprops) != 0) { - /* XXX TODO: Real errors */ - fprintf(stderr, - "bectl list: failed to fetch origin properties\n"); - return; - } - } + get_origin_props(dsprops, &originprops); if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { space = strtoull(propstr, NULL, 10); - if (originprops != NULL && nvlist_lookup_string(originprops, - "used", &propstr) == 0) + if (!pc->show_all_datasets && originprops != NULL && + nvlist_lookup_string(originprops, "used", &propstr) == 0) space += strtoull(propstr, NULL, 10); /* Alas, there's more to it,. */ @@ -494,36 +539,84 @@ print_dataset(nvpair_t *cur, struct printc *pc) } printf("\n"); - if (originprops != NULL) + if (originprops != NULL) { + /*if (pc->show_all_datasets) { + }*/ be_prop_list_free(originprops); + } #undef BUFSZ } +static void +print_headers(nvlist_t *props, struct printc *pc) +{ + const char *chosen_be_header; + nvpair_t *cur; + nvlist_t *dsprops; + char *propstr; + size_t be_maxcol; + + if (pc->show_all_datasets || pc->show_snaps) + chosen_be_header = HEADER_BEPLUS; + else + chosen_be_header = HEADER_BE; + be_maxcol = strlen(chosen_be_header); + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur))); + if (!pc->show_all_datasets && !pc->show_snaps) + continue; + nvpair_value_nvlist(cur, &dsprops); + if (nvlist_lookup_string(dsprops, "dataset", &propstr) != 0) + continue; + be_maxcol = MAX(be_maxcol, strlen(propstr) + INDENT_INCREMENT); + if (nvlist_lookup_string(dsprops, "origin", &propstr) != 0) + continue; + be_maxcol = MAX(be_maxcol, + strlen(propstr) + INDENT_INCREMENT * 2); + } + + pc->be_colsz = -be_maxcol; + /* To be made negative after calculating final col sz */ + pc->active_colsz_def = strlen(HEADER_ACTIVE); + pc->mount_colsz = -(int)strlen(HEADER_MOUNT); + pc->space_colsz = -(int)strlen(HEADER_SPACE); + /* XXX TODO: Take -H into account */ + printf("%*s %s %s %s %s\n", pc->be_colsz, chosen_be_header, + HEADER_ACTIVE, HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); + + /* + * All other invocations in which we aren't using the default header + * will produce quite a bit of input. Throw an extra blank line after + * the header to make it look nicer. + */ + if (chosen_be_header != HEADER_BE) + printf("\n"); +} + static int bectl_cmd_list(int argc, char *argv[]) { struct printc pc; nvpair_t *cur; - nvlist_t *props; - size_t be_maxcol; + nvlist_t *dsprops, *props; int opt; - bool show_all_datasets, show_space, hide_headers, show_snaps; props = NULL; - show_all_datasets = show_space = hide_headers = show_snaps = false; + bzero(&pc, sizeof(pc)); while ((opt = getopt(argc, argv, "aDHs")) != -1) { switch (opt) { case 'a': - show_all_datasets = true; + pc.show_all_datasets = true; break; case 'D': - show_space = true; + pc.show_space = true; break; case 'H': - hide_headers = true; + pc.hide_headers = true; break; case 's': - show_space = true; + pc.show_snaps = true; break; default: fprintf(stderr, "bectl list: unknown option '-%c'\n", @@ -549,22 +642,12 @@ bectl_cmd_list(int argc, char *argv[]) return (1); } - be_maxcol = strlen(HEADER_BE); + print_headers(props, &pc); for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; cur = nvlist_next_nvpair(props, cur)) { - be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur))); - } - - pc.be_colsz = -be_maxcol; - /* To be made negative after calculating final col sz */ - pc.active_colsz_def = strlen(HEADER_ACTIVE); - pc.mount_colsz = -(int)strlen(HEADER_MOUNT); - pc.space_colsz = -(int)strlen(HEADER_SPACE); - printf("%*s %s %s %s %s\n", pc.be_colsz, HEADER_BE, HEADER_ACTIVE, - HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); - for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; - cur = nvlist_next_nvpair(props, cur)) { - print_dataset(cur, &pc); + nvpair_value_nvlist(cur, &dsprops); + pc.final_be = nvlist_next_nvpair(props, cur) == NULL; + print_info(nvpair_name(cur), dsprops, &pc); } be_prop_list_free(props); From 344342064652974127eea595ac5aed37dd31c20d Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sat, 4 Aug 2018 06:29:46 +0000 Subject: [PATCH 033/322] bectl(8): Sort BEs lightly by active (now or later) BE, then others While it could be preferred to do this at insert in libbe(3), there's no convenient way to insert at the head of an nvlist. Instead, we'll make two passes over- once to print anything active either now or at nextboot, and another to print everything else. This doesn't actually impact performance in a significant way here, so we'll worry about further optimizations if the need actually arises. --- sbin/bectl/bectl.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 59174d8ad5fa..9764ea6e443c 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -59,7 +59,6 @@ struct printc { int current_indent; int mount_colsz; int space_colsz; - bool final_be; bool hide_headers; bool show_all_datasets; bool show_snaps; @@ -489,8 +488,6 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) nvlist_free(originprops); } pc->current_indent = 0; - if (!pc->final_be) - printf("\n"); return; } @@ -600,9 +597,11 @@ bectl_cmd_list(int argc, char *argv[]) struct printc pc; nvpair_t *cur; nvlist_t *dsprops, *props; - int opt; + int opt, printed; + boolean_t active_now, active_reboot; props = NULL; + printed = 0; bzero(&pc, sizeof(pc)); while ((opt = getopt(argc, argv, "aDHs")) != -1) { switch (opt) { @@ -643,11 +642,38 @@ bectl_cmd_list(int argc, char *argv[]) } print_headers(props, &pc); + /* Do a first pass to print active and next active first */ for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; cur = nvlist_next_nvpair(props, cur)) { nvpair_value_nvlist(cur, &dsprops); - pc.final_be = nvlist_next_nvpair(props, cur) == NULL; + active_now = active_reboot = false; + + nvlist_lookup_boolean_value(dsprops, "active", &active_now); + nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot); + if (!active_now && !active_reboot) + continue; + if (printed > 0 && (pc.show_all_datasets || pc.show_snaps)) + printf("\n"); print_info(nvpair_name(cur), dsprops, &pc); + printed++; + } + + /* Now pull everything else */ + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + nvpair_value_nvlist(cur, &dsprops); + active_now = active_reboot = false; + + nvlist_lookup_boolean_value(dsprops, "active", &active_now); + nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot); + if (active_now || active_reboot) + continue; + if (printed > 0 && (pc.show_all_datasets || pc.show_snaps)) + printf("\n"); + print_info(nvpair_name(cur), dsprops, &pc); + printed++; } be_prop_list_free(props); From f97b318f7d23f2ef500a7342fe050ddac9424460 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sun, 5 Aug 2018 04:16:00 +0000 Subject: [PATCH 034/322] bectl(8): Take -H parameter to list into account -H is for a scripted format, where all fields are tab-delimited and the headers go away. We accomplish this by splitting out pad printing to a separate function that'll take into account whether we're supposed to be scripted or not. This has the nice side effect of maintaining positive column sizes again. --- sbin/bectl/bectl.c | 59 ++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 9764ea6e443c..44bc8a0bcf0f 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -59,7 +59,7 @@ struct printc { int current_indent; int mount_colsz; int space_colsz; - bool hide_headers; + bool script_fmt; bool show_all_datasets; bool show_snaps; bool show_space; @@ -73,6 +73,7 @@ static int bectl_cmd_import(int argc, char *argv[]); static int bectl_cmd_add(int argc, char *argv[]); static int bectl_cmd_jail(int argc, char *argv[]); static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops); +static void print_padding(const char *fval, int colsz, struct printc *pc); static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc); static void print_headers(nvlist_t *props, struct printc *pc); static int bectl_cmd_list(int argc, char *argv[]); @@ -456,6 +457,19 @@ get_origin_props(nvlist_t *dsprops, nvlist_t **originprops) return (NULL); } +static void +print_padding(const char *fval, int colsz, struct printc *pc) +{ + + if (pc->script_fmt) { + printf("\t"); + return; + } + + if (fval != NULL) + colsz -= strlen(fval); + printf("%*s ", colsz, ""); +} static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc) @@ -470,8 +484,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) boolean_t active_now, active_reboot; originprops = NULL; - printf("%*s%*s ", pc->current_indent, "", - pc->be_colsz + pc->current_indent, name); + printf("%*s%s", pc->current_indent, "", name); /* Recurse at the base level if we're breaking info down */ if (pc->current_indent == 0 && (pc->show_all_datasets || @@ -489,7 +502,8 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) } pc->current_indent = 0; return; - } + } else + print_padding(name, pc->be_colsz - pc->current_indent, pc); active_colsz = pc->active_colsz_def; if (nvlist_lookup_boolean_value(dsprops, "active", @@ -506,11 +520,14 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) printf("-"); active_colsz--; } - printf("%*s ", -active_colsz, " "); - if (nvlist_lookup_string(dsprops, "mountpoint", &propstr) == 0) - printf("%*s ", pc->mount_colsz, propstr); - else - printf("%*s ", pc->mount_colsz, "-"); + print_padding(NULL, active_colsz, pc); + if (nvlist_lookup_string(dsprops, "mountpoint", &propstr) == 0){ + printf("%s", propstr); + print_padding(propstr, pc->mount_colsz, pc); + } else { + printf("%s", "-"); + print_padding("-", pc->mount_colsz, pc); + } get_origin_props(dsprops, &originprops); @@ -524,9 +541,12 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) /* Alas, there's more to it,. */ humanize_number(buf, 6, space, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B); - printf("%*s ", pc->space_colsz, buf); - } else - printf("%*s ", pc->space_colsz, "-"); + printf("%s", buf); + print_padding(buf, pc->space_colsz, pc); + } else { + printf("%s", "-"); + print_padding("-", pc->space_colsz, pc); + } if (nvlist_lookup_string(dsprops, "creation", &propstr) == 0) { ctimenum = strtoull(propstr, NULL, 10); @@ -573,13 +593,11 @@ print_headers(nvlist_t *props, struct printc *pc) strlen(propstr) + INDENT_INCREMENT * 2); } - pc->be_colsz = -be_maxcol; - /* To be made negative after calculating final col sz */ + pc->be_colsz = be_maxcol; pc->active_colsz_def = strlen(HEADER_ACTIVE); - pc->mount_colsz = -(int)strlen(HEADER_MOUNT); - pc->space_colsz = -(int)strlen(HEADER_SPACE); - /* XXX TODO: Take -H into account */ - printf("%*s %s %s %s %s\n", pc->be_colsz, chosen_be_header, + pc->mount_colsz = strlen(HEADER_MOUNT); + pc->space_colsz = strlen(HEADER_SPACE); + printf("%*s %s %s %s %s\n", -pc->be_colsz, chosen_be_header, HEADER_ACTIVE, HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); /* @@ -612,7 +630,7 @@ bectl_cmd_list(int argc, char *argv[]) pc.show_space = true; break; case 'H': - pc.hide_headers = true; + pc.script_fmt = true; break; case 's': pc.show_snaps = true; @@ -641,7 +659,8 @@ bectl_cmd_list(int argc, char *argv[]) return (1); } - print_headers(props, &pc); + if (!pc.script_fmt) + print_headers(props, &pc); /* Do a first pass to print active and next active first */ for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; cur = nvlist_next_nvpair(props, cur)) { From 9b1662e67b54e3a4ae04050beacc38645a003a8c Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sun, 5 Aug 2018 04:40:13 +0000 Subject: [PATCH 035/322] bectl: Implement -D ("space if origin datasets were deleted") This also accomplishes the following: - Proxy through zfs_nicenum as be_nicenum, because it looks better than humanize_number and would presumably be useful to other libbe consumers. - Rename be_get_snapshot_props to be_get_dataset_props, make it more useful --- lib/libbe/be.c | 9 +++++++++ lib/libbe/be.h | 3 ++- lib/libbe/be_info.c | 4 ++-- lib/libbe/libbe.3 | 5 ++++- sbin/bectl/bectl.c | 45 +++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 48bb36c2beea..4e889abee2e4 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -171,6 +171,15 @@ libbe_close(libbe_handle_t *lbh) free(lbh); } +/* + * Proxy through to libzfs for the moment. + */ +void +be_nicenum(uint64_t num, char *buf, size_t buflen) +{ + + zfs_nicenum(num, buf, buflen); +} /* * Destroy the boot environment or snapshot specified by the name diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 988baaf16b08..e220626e6af2 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -67,7 +67,7 @@ const char *be_nextboot_path(libbe_handle_t *); const char *be_root_path(libbe_handle_t *); int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); -int be_get_snapshot_props(libbe_handle_t *, const char *, nvlist_t *); +int be_get_dataset_props(libbe_handle_t *, const char *, nvlist_t *); int be_prop_list_alloc(nvlist_t **be_list); void be_prop_list_free(nvlist_t *be_list); @@ -116,5 +116,6 @@ int be_export(libbe_handle_t *, char *, int fd); int be_import(libbe_handle_t *, char *, int fd); int be_add_child(libbe_handle_t *, char *, bool); +void be_nicenum(uint64_t num, char *buf, size_t buflen); #endif /* _LIBBE_H */ diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index c4059ea5d598..49f64151d034 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -99,7 +99,7 @@ be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) } int -be_get_snapshot_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) +be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) { zfs_handle_t *snap_hdl; prop_data_t data; @@ -109,7 +109,7 @@ be_get_snapshot_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) data.list = props; data.single_object = true; if ((snap_hdl = zfs_open(lbh->lzh, name, - ZFS_TYPE_SNAPSHOT)) == NULL) + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) return (BE_ERR_ZFSOPEN); ret = prop_list_builder_cb(snap_hdl, &data); diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index 365a1e85dc66..cdd4ab7ae653 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -88,6 +88,9 @@ of state to be retained, such as errors from previous operations. .Ft int .Fn be_destroy "libbe_handle_t *, char *, int" ; .Pp +.Ft void +.Fn be_nicenum "uint64_t, char *, size_t" ; +.Pp .\" TODO: Write up of mount options .\" typedef enum { .\" BE_MNT_FORCE = 1 << 0, @@ -139,7 +142,7 @@ of state to be retained, such as errors from previous operations. .Fn be_get_bootenv_props "libbe_handle_t *, nvlist_t *" ; .Pp .Ft int -.Fn be_get_snapshot_props "libbe_handle_t *, const char *, nvlist_t *" ; +.Fn be_get_dataset_props "libbe_handle_t *, const char *, nvlist_t *" ; .Pp .Ft void .Fn be_prop_list_free "nvlist_t *" ; diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 44bc8a0bcf0f..f726f74596df 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -445,7 +445,7 @@ get_origin_props(nvlist_t *dsprops, nvlist_t **originprops) "bectl list: failed to allocate origin prop nvlist\n"); return (NULL); } - if (be_get_snapshot_props(be, propstr, *originprops) != 0) { + if (be_get_dataset_props(be, propstr, *originprops) != 0) { /* XXX TODO: Real errors */ fprintf(stderr, "bectl list: failed to fetch origin properties\n"); @@ -471,6 +471,40 @@ print_padding(const char *fval, int colsz, struct printc *pc) printf("%*s ", colsz, ""); } +static unsigned long long +dataset_space(const char *oname) +{ + unsigned long long space; + char *dsname, *propstr, *sep; + nvlist_t *dsprops; + + space = 0; + dsname = strdup(oname); + if (dsname == NULL) + return (0); + + if ((sep = strchr(dsname, '@')) != NULL) + *sep = '\0'; + + if (be_prop_list_alloc(&dsprops) != 0) { + free(dsname); + return (0); + } + + if (be_get_dataset_props(be, dsname, dsprops) != 0) { + nvlist_free(dsprops); + free(dsname); + return (0); + } + + if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) + space = strtoull(propstr, NULL, 10); + + nvlist_free(dsprops); + free(dsname); + return (space); +} + static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc) { @@ -529,7 +563,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) print_padding("-", pc->mount_colsz, pc); } - get_origin_props(dsprops, &originprops); + oname = get_origin_props(dsprops, &originprops); if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { space = strtoull(propstr, NULL, 10); @@ -538,9 +572,12 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) nvlist_lookup_string(originprops, "used", &propstr) == 0) space += strtoull(propstr, NULL, 10); + if (!pc->show_all_datasets && pc->show_space && oname != NULL) + /* Get the used space of the origin's dataset, too. */ + space += dataset_space(oname); + /* Alas, there's more to it,. */ - humanize_number(buf, 6, space, "", HN_AUTOSCALE, - HN_DECIMAL | HN_NOSPACE | HN_B); + be_nicenum(space, buf, 6); printf("%s", buf); print_padding(buf, pc->space_colsz, pc); } else { From 96c5db5854191eb7ff464c74ea677ee8016b3a9e Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sun, 5 Aug 2018 19:38:56 +0000 Subject: [PATCH 036/322] bectl(8): Implement `bectl list -s` be_get_dataset_snapshots has been added to libbe(3), effectively returning the same information as be_get_bootenv_props but for snapshots of the given dataset. The assumption is that one will have the BE dataset name before wanting to grab snapshots. --- lib/libbe/be.h | 1 + lib/libbe/be_info.c | 28 ++++++++++++++++++++++++++++ lib/libbe/libbe.3 | 3 +++ sbin/bectl/bectl.c | 41 ++++++++++++++++++++++++++++++++++------- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/lib/libbe/be.h b/lib/libbe/be.h index e220626e6af2..a4d0f3954406 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -68,6 +68,7 @@ const char *be_root_path(libbe_handle_t *); int be_get_bootenv_props(libbe_handle_t *, nvlist_t *); int be_get_dataset_props(libbe_handle_t *, const char *, nvlist_t *); +int be_get_dataset_snapshots(libbe_handle_t *, const char *, nvlist_t *); int be_prop_list_alloc(nvlist_t **be_list); void be_prop_list_free(nvlist_t *be_list); diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 49f64151d034..1a12b2d58994 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -29,6 +29,8 @@ #include "be.h" #include "be_impl.h" +static int snapshot_prop_list_builder(zfs_handle_t *hdl, prop_data_t *data); + /* * Returns the name of the active boot environment */ @@ -117,6 +119,25 @@ be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) return (ret); } +int +be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props) +{ + zfs_handle_t *ds_hdl; + prop_data_t data; + int ret; + + data.lbh = lbh; + data.list = props; + data.single_object = false; + if ((ds_hdl = zfs_open(lbh->lzh, name, + ZFS_TYPE_FILESYSTEM)) == NULL) + return (BE_ERR_ZFSOPEN); + + ret = snapshot_prop_list_builder(ds_hdl, &data); + zfs_close(ds_hdl); + return (ret); +} + /* * Internal callback function used by zfs_iter_filesystems. For each dataset in * the bootenv root, populate an nvlist_t of its relevant properties. @@ -221,6 +242,13 @@ prop_list_builder(prop_data_t *data) return (0); } +static int +snapshot_prop_list_builder(zfs_handle_t *hdl, prop_data_t *data) +{ + + return (zfs_iter_snapshots_sorted(hdl, prop_list_builder_cb, data)); +} + int be_prop_list_alloc(nvlist_t **be_list) diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index cdd4ab7ae653..b6cf5a460112 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -144,6 +144,9 @@ of state to be retained, such as errors from previous operations. .Ft int .Fn be_get_dataset_props "libbe_handle_t *, const char *, nvlist_t *" ; .Pp +.Ft int +.Fn be_get_dataset_snapshots "libbe_handle_t *, const char *, nvlist_t *" ; +.Pp .Ft void .Fn be_prop_list_free "nvlist_t *" ; .\" .Ft void diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index f726f74596df..d9d506b5cc6e 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -74,6 +74,7 @@ static int bectl_cmd_add(int argc, char *argv[]); static int bectl_cmd_jail(int argc, char *argv[]); static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops); static void print_padding(const char *fval, int colsz, struct printc *pc); +static int print_snapshots(const char *dsname, struct printc *pc); static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc); static void print_headers(nvlist_t *props, struct printc *pc); static int bectl_cmd_list(int argc, char *argv[]); @@ -505,6 +506,28 @@ dataset_space(const char *oname) return (space); } +static int +print_snapshots(const char *dsname, struct printc *pc) +{ + nvpair_t *cur; + nvlist_t *props, *sprops; + + if (be_prop_list_alloc(&props) != 0) { + fprintf(stderr, "bectl list: failed to allocate snapshot nvlist\n"); + return (1); + } + if (be_get_dataset_snapshots(be, dsname, props) != 0) { + fprintf(stderr, "bectl list: failed to fetch boot ds snapshots\n"); + return (1); + } + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + nvpair_value_nvlist(cur, &sprops); + print_info(nvpair_name(cur), sprops, pc); + } + return (0); +} + static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc) { @@ -513,27 +536,34 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) unsigned long long ctimenum, space; nvlist_t *originprops; const char *oname; - char *propstr; + char *dsname, *propstr; int active_colsz; boolean_t active_now, active_reboot; + dsname = NULL; originprops = NULL; printf("%*s%s", pc->current_indent, "", name); + nvlist_lookup_string(dsprops, "dataset", &dsname); /* Recurse at the base level if we're breaking info down */ if (pc->current_indent == 0 && (pc->show_all_datasets || pc->show_snaps)) { printf("\n"); - if (nvlist_lookup_string(dsprops, "dataset", &propstr) != 0) + if (dsname == NULL) /* XXX TODO: Error? */ return; pc->current_indent += INDENT_INCREMENT; - print_info(propstr, dsprops, pc); + print_info(dsname, dsprops, pc); pc->current_indent += INDENT_INCREMENT; if ((oname = get_origin_props(dsprops, &originprops)) != NULL) { print_info(oname, originprops, pc); nvlist_free(originprops); } + + /* Back up a level; snapshots at the same level as dataset */ + pc->current_indent -= INDENT_INCREMENT; + if (pc->show_snaps) + print_snapshots(dsname, pc); pc->current_indent = 0; return; } else @@ -593,11 +623,8 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) } printf("\n"); - if (originprops != NULL) { - /*if (pc->show_all_datasets) { - }*/ + if (originprops != NULL) be_prop_list_free(originprops); - } #undef BUFSZ } From 9a460e6fd2d552fe5906342d16552ba39510bd12 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Sun, 5 Aug 2018 20:03:05 +0000 Subject: [PATCH 037/322] bectl(8): Some light cleanup and commenting --- sbin/bectl/bectl.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index d9d506b5cc6e..653ec4a0033b 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -462,6 +462,7 @@ static void print_padding(const char *fval, int colsz, struct printc *pc) { + /* -H flag handling; all delimiters/padding are a single tab */ if (pc->script_fmt) { printf("\t"); return; @@ -484,6 +485,7 @@ dataset_space(const char *oname) if (dsname == NULL) return (0); + /* Truncate snapshot to dataset name, as needed */ if ((sep = strchr(dsname, '@')) != NULL) *sep = '\0'; @@ -552,6 +554,12 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) if (dsname == NULL) /* XXX TODO: Error? */ return; + /* + * Whether we're dealing with -a or -s, we'll always print the + * dataset name/information followed by its origin. For -s, we + * additionally iterate through all snapshots of this boot + * environment and also print their information. + */ pc->current_indent += INDENT_INCREMENT; print_info(dsname, dsprops, pc); pc->current_indent += INDENT_INCREMENT; @@ -594,16 +602,23 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) } oname = get_origin_props(dsprops, &originprops); - if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { + /* + * The space used column is some composition of: + * - The "used" property of the dataset + * - The "used" property of the origin snapshot (not -a or -s) + * - The "used" property of the origin dataset (-D flag only) + * + * The -D flag is ignored if -a or -s are specified. + */ space = strtoull(propstr, NULL, 10); - if (!pc->show_all_datasets && originprops != NULL && + if (!pc->show_all_datasets && !pc->show_snaps && + originprops != NULL && nvlist_lookup_string(originprops, "used", &propstr) == 0) space += strtoull(propstr, NULL, 10); - if (!pc->show_all_datasets && pc->show_space && oname != NULL) - /* Get the used space of the origin's dataset, too. */ + if (pc->show_space && oname != NULL) space += dataset_space(oname); /* Alas, there's more to it,. */ @@ -611,7 +626,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) printf("%s", buf); print_padding(buf, pc->space_colsz, pc); } else { - printf("%s", "-"); + printf("-"); print_padding("-", pc->space_colsz, pc); } @@ -723,6 +738,9 @@ bectl_cmd_list(int argc, char *argv[]) return (1); } + /* Force -D off if either -a or -s are specified */ + if (pc.show_all_datasets || pc.show_snaps) + pc.show_space = false; if (!pc.script_fmt) print_headers(props, &pc); /* Do a first pass to print active and next active first */ From d694059f1eb0aff0647bd119586daa0c160fe330 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Mon, 6 Aug 2018 03:32:25 +0000 Subject: [PATCH 038/322] bectl(8): bectl jail improvements - Support passing arbitrary jail arguments via -o - Split the related (and rewritten since the GSoC) jail bits out into a new bectl_jail.c file, to reduce clutter in bectl.c - Don't use RFC 1918 IP space [0]; we'll instead set no default IPv4 and let the user pass in any address options they wish via -o Reported by: rgrimes [0], Shawn Webb [0] --- sbin/bectl/Makefile | 2 + sbin/bectl/bectl.8 | 25 +++- sbin/bectl/bectl.c | 165 +-------------------- sbin/bectl/bectl.h | 35 +++++ sbin/bectl/bectl_jail.c | 312 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 378 insertions(+), 161 deletions(-) create mode 100644 sbin/bectl/bectl.h create mode 100644 sbin/bectl/bectl_jail.c diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index 5d1913f03f3a..a11ebc4f2072 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -3,6 +3,8 @@ PROG= bectl MAN= bectl.8 +SRCS= bectl.c bectl_jail.c + LIBADD+= be LIBADD+= jail LIBADD+= nvpair diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index 23843fd85cfc..a55e9af8aaf5 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -14,7 +14,7 @@ .\" @(#)be.1 .\" $FreeBSD$ .\" -.Dd July 24, 2018 +.Dd August 5, 2018 .Dt BECTL 8 .Os FreeBSD .Sh NAME @@ -40,6 +40,7 @@ destroy .Ao Ar beName | beName@snapshot Ac .Nm jail +.Op Fl o Ar key Ns = Ns Ar value Oc Ns ... .Ao Ar jailID | jailName Ac .Ao Ar bootenv Ac .Nm @@ -123,10 +124,32 @@ Specifying will automatically unmount without confirmation. .Pp .It Ic jail +.Op Fl o Ar key Ns = Ns Ar value Oc Ns ... .Ao Ar jailID | jailName Ac .Ao Ar bootenv Ac .Pp Creates a jail of the given boot environment. +Multiple +.Fl o +arguments may be specified. +Al +.Ar key , +.Ar value +pairs are interpreted as jail parameters as described in +.Xr jail 8 . +The following default parameters are provided: +.Bl -tag -width -indent +.It Va allow.mount Ns = Ns Ar true +.It Va allow.mount.devfs Ns = Ns Ar true +.It Va enforce_statfs Ns = Ns Ar 1 +.It Va name Ns = Ns Ar bootenv +.It Va host.hostname Ns = Ns Ar bootenv +.It Va path +Set to a path in /tmp generated by +.Xr libbe 8 . +.El +.pp +All default parameters may be overwritten. .Pp .It Ic list .Op Fl a diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 653ec4a0033b..8728b0e55c22 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -27,10 +27,8 @@ */ #include -#include #include #include -#include #include #include #include @@ -43,6 +41,8 @@ #include +#include "bectl.h" + #define HEADER_BE "BE" #define HEADER_BEPLUS "BE/Dataset/Snapshot" #define HEADER_ACTIVE "Active" @@ -71,7 +71,6 @@ static int bectl_cmd_destroy(int argc, char *argv[]); static int bectl_cmd_export(int argc, char *argv[]); static int bectl_cmd_import(int argc, char *argv[]); static int bectl_cmd_add(int argc, char *argv[]); -static int bectl_cmd_jail(int argc, char *argv[]); static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops); static void print_padding(const char *fval, int colsz, struct printc *pc); static int print_snapshots(const char *dsname, struct printc *pc); @@ -80,14 +79,11 @@ static void print_headers(nvlist_t *props, struct printc *pc); static int bectl_cmd_list(int argc, char *argv[]); static int bectl_cmd_mount(int argc, char *argv[]); static int bectl_cmd_rename(int argc, char *argv[]); -static int bectl_search_jail_paths(const char *mnt); -static int bectl_locate_jail(const char *ident); -static int bectl_cmd_unjail(int argc, char *argv[]); static int bectl_cmd_unmount(int argc, char *argv[]); -static libbe_handle_t *be; +libbe_handle_t *be; -static int +int usage(bool explicit) { FILE *fp; @@ -102,7 +98,7 @@ usage(bool explicit) "\tbectl export sourceBe\n" "\tbectl import targetBe\n" "\tbectl add (path)*\n" - "\tbectl jail bootenv\n" + "\tbectl jail [ -o key=value ]... bootenv\n" "\tbectl list [-a] [-D] [-H] [-s]\n" "\tbectl mount beName [mountpoint]\n" "\tbectl rename origBeName newBeName\n" @@ -379,58 +375,6 @@ bectl_cmd_destroy(int argc, char *argv[]) return (err); } - -static int -bectl_cmd_jail(int argc, char *argv[]) -{ - char *bootenv; - char mnt_loc[BE_MAXPATHLEN]; - int err, jid; - - /* struct jail be_jail = { 0 }; */ - - if (argc == 1) { - fprintf(stderr, "bectl jail: missing boot environment name\n"); - return (usage(false)); - } - if (argc > 2) { - fprintf(stderr, "bectl jail: too many arguments\n"); - return (usage(false)); - } - - bootenv = argv[1]; - - /* - * XXX TODO: if its already mounted, perhaps there should be a flag to - * indicate its okay to proceed?? - */ - if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) { - fprintf(stderr, "could not mount bootenv\n"); - return (1); - } - - /* XXX TODO: Make the IP/hostname configurable? */ - jid = jail_setv(JAIL_CREATE | JAIL_ATTACH, - "name", bootenv, - "path", mnt_loc, - "host.hostname", bootenv, - "persist", "true", - "ip4.addr", "10.20.30.40", - "allow.mount", "true", - "allow.mount.devfs", "true", - "enforce_statfs", "1", - NULL); - if (jid == -1) { - fprintf(stderr, "unable to create jail. error: %d\n", errno); - return (1); - } - - /* We're attached within the jail... good bye! */ - chdir("/"); - execl("/bin/sh", "/bin/sh", NULL); - return (0); -} - /* * Given a set of dataset properties (for a BE dataset), populate originprops * with the origin's properties. @@ -851,105 +795,6 @@ bectl_cmd_rename(int argc, char *argv[]) return (0); } -static int -bectl_search_jail_paths(const char *mnt) -{ - char jailpath[MAXPATHLEN + 1]; - int jid; - - jid = 0; - (void)mnt; - while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath, - NULL)) != -1) { - if (strcmp(jailpath, mnt) == 0) - return (jid); - } - - return (-1); -} - -/* - * Locate a jail based on an arbitrary identifier. This may be either a name, - * a jid, or a BE name. Returns the jid or -1 on failure. - */ -static int -bectl_locate_jail(const char *ident) -{ - nvlist_t *belist, *props; - char *mnt; - int jid; - - /* Try the easy-match first */ - jid = jail_getid(ident); - if (jid != -1) - return (jid); - - /* Attempt to try it as a BE name, first */ - if (be_prop_list_alloc(&belist) != 0) - return (-1); - - if (be_get_bootenv_props(be, belist) != 0) - return (-1); - - if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { - /* We'll attempt to resolve the jid by way of mountpoint */ - if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) { - jid = bectl_search_jail_paths(mnt); - be_prop_list_free(belist); - return (jid); - } - - be_prop_list_free(belist); - } - - return (-1); -} - -static int -bectl_cmd_unjail(int argc, char *argv[]) -{ - char path[MAXPATHLEN + 1]; - char *cmd, *name, *target; - int jid; - - /* Store alias used */ - cmd = argv[0]; - - if (argc != 2) { - fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); - return (usage(false)); - } - - target = argv[1]; - - /* Locate the jail */ - if ((jid = bectl_locate_jail(target)) == -1) { - fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, target); - return (1); - } - - bzero(&path, MAXPATHLEN + 1); - name = jail_getname(jid); - if (jail_getv(0, "name", name, "path", path, NULL) != jid) { - free(name); - fprintf(stderr, "bectl %s: failed to get path for jail requested by '%s'\n", cmd, target); - return (1); - } - - free(name); - - if (be_mounted_at(be, path, NULL) != 0) { - fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", cmd, target); - return (1); - } - - jail_remove(jid); - unmount(path, 0); - - return (0); -} - - static int bectl_cmd_unmount(int argc, char *argv[]) { diff --git a/sbin/bectl/bectl.h b/sbin/bectl/bectl.h new file mode 100644 index 000000000000..3b8abb8aaaad --- /dev/null +++ b/sbin/bectl/bectl.h @@ -0,0 +1,35 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Kyle Evans + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int usage(bool explicit); + +int bectl_cmd_jail(int argc, char *argv[]); +int bectl_cmd_unjail(int argc, char *argv[]); + +extern libbe_handle_t *be; diff --git a/sbin/bectl/bectl_jail.c b/sbin/bectl/bectl_jail.c new file mode 100644 index 000000000000..b012a906c5e2 --- /dev/null +++ b/sbin/bectl/bectl_jail.c @@ -0,0 +1,312 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Kyle Evans + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "bectl.h" + +static void jailparam_grow(void); +static void jailparam_add(const char *name, const char *val); +static bool jailparam_addarg(char *arg); + +static int bectl_search_jail_paths(const char *mnt); +static int bectl_locate_jail(const char *ident); + +/* We'll start with 8 parameters initially and grow as needed. */ +#define INIT_PARAMCOUNT 8 + +static struct jailparam *jp; +static int jpcnt; +static int jpused; +static char mnt_loc[BE_MAXPATHLEN + 1]; + +static void +jailparam_grow(void) +{ + + jpcnt *= 2; + jp = realloc(jp, jpcnt * sizeof(*jp)); + if (jp == NULL) + err(2, "realloc"); +} + +static void +jailparam_add(const char *name, const char *val) +{ + int i; + + for (i = 0; i < jpused; ++i) { + if (strcmp(name, jp[i].jp_name) == 0) + break; + } + + if (i < jpused) + jailparam_free(&jp[i], 1); + else if (jpused == jpcnt) + /* The next slot isn't allocated yet */ + jailparam_grow(); + + if (jailparam_init(&jp[i], name) != 0) + return; + if (jailparam_import(&jp[i], val) != 0) + return; + ++jpused; +} + +static bool +jailparam_addarg(char *arg) +{ + char *name, *val; + + if (arg == NULL) + return (false); + name = arg; + if ((val = strchr(arg, '=')) == NULL) { + fprintf(stderr, "bectl jail: malformed jail option '%s'\n", + arg); + return (false); + } + + *val++ = '\0'; + if (strcmp(name, "path") == 0) { + if (strlen(val) > BE_MAXPATHLEN) { + fprintf(stderr, + "bectl jail: skipping too long path assignment '%s' (max length = %d)\n", + val, BE_MAXPATHLEN); + return (false); + } + strcpy(mnt_loc, val); + } + jailparam_add(name, val); + return (true); +} + +int +bectl_cmd_jail(int argc, char *argv[]) +{ + char *bootenv, *mountpoint; + int jid, opt; + bool default_hostname, default_name; + + default_hostname = default_name = true; + jpcnt = INIT_PARAMCOUNT; + jp = malloc(jpcnt * sizeof(*jp)); + if (jp == NULL) + err(2, "malloc"); + + jailparam_add("persist", "true"); + jailparam_add("allow.mount", "true"); + jailparam_add("allow.mount.devfs", "true"); + jailparam_add("enforce_statfs", "1"); + + while ((opt = getopt(argc, argv, "o:")) != -1) { + switch (opt) { + case 'o': + if (jailparam_addarg(optarg)) { + /* + * optarg has been modified to null terminate + * at the assignment operator. + */ + if (strcmp(optarg, "name") == 0) + default_name = false; + if (strcmp(optarg, "host.hostname") == 0) + default_hostname = false; + } + break; + default: + fprintf(stderr, "bectl jail: unknown option '-%c'\n", + optopt); + return (usage(false)); + } + } + + argc -= optind; + argv += optind; + + /* struct jail be_jail = { 0 }; */ + if (argc < 1) { + fprintf(stderr, "bectl jail: missing boot environment name\n"); + return (usage(false)); + } + if (argc > 2) { + fprintf(stderr, "bectl jail: too many arguments\n"); + return (usage(false)); + } + + bootenv = argv[0]; + + /* + * XXX TODO: if its already mounted, perhaps there should be a flag to + * indicate its okay to proceed?? + */ + if (*mnt_loc == '\0') + mountpoint = NULL; + else + mountpoint = mnt_loc; + if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) { + fprintf(stderr, "could not mount bootenv\n"); + return (1); + } + + if (default_name) + jailparam_add("name", bootenv); + if (default_hostname) + jailparam_add("host.hostname", bootenv); + /* + * This is our indicator that path was not set by the user, so we'll use + * the path that libbe generated for us. + */ + if (mountpoint == NULL) + jailparam_add("path", mnt_loc); + jid = jailparam_set(jp, jpused, JAIL_CREATE | JAIL_ATTACH); + if (jid == -1) { + fprintf(stderr, "unable to create jail. error: %d\n", errno); + return (1); + } + + jailparam_free(jp, jpused); + free(jp); + + /* We're attached within the jail... good bye! */ + chdir("/"); + execl("/bin/sh", "/bin/sh", NULL); + return (0); +} + +static int +bectl_search_jail_paths(const char *mnt) +{ + char jailpath[MAXPATHLEN + 1]; + int jid; + + jid = 0; + (void)mnt; + while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath, + NULL)) != -1) { + if (strcmp(jailpath, mnt) == 0) + return (jid); + } + + return (-1); +} + +/* + * Locate a jail based on an arbitrary identifier. This may be either a name, + * a jid, or a BE name. Returns the jid or -1 on failure. + */ +static int +bectl_locate_jail(const char *ident) +{ + nvlist_t *belist, *props; + char *mnt; + int jid; + + /* Try the easy-match first */ + jid = jail_getid(ident); + if (jid != -1) + return (jid); + + /* Attempt to try it as a BE name, first */ + if (be_prop_list_alloc(&belist) != 0) + return (-1); + + if (be_get_bootenv_props(be, belist) != 0) + return (-1); + + if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { + /* We'll attempt to resolve the jid by way of mountpoint */ + if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) { + jid = bectl_search_jail_paths(mnt); + be_prop_list_free(belist); + return (jid); + } + + be_prop_list_free(belist); + } + + return (-1); +} + +int +bectl_cmd_unjail(int argc, char *argv[]) +{ + char path[MAXPATHLEN + 1]; + char *cmd, *name, *target; + int jid; + + /* Store alias used */ + cmd = argv[0]; + + if (argc != 2) { + fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); + return (usage(false)); + } + + target = argv[1]; + + /* Locate the jail */ + if ((jid = bectl_locate_jail(target)) == -1) { + fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, + target); + return (1); + } + + bzero(&path, MAXPATHLEN + 1); + name = jail_getname(jid); + if (jail_getv(0, "name", name, "path", path, NULL) != jid) { + free(name); + fprintf(stderr, + "bectl %s: failed to get path for jail requested by '%s'\n", + cmd, target); + return (1); + } + + free(name); + + if (be_mounted_at(be, path, NULL) != 0) { + fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", + cmd, target); + return (1); + } + + jail_remove(jid); + unmount(path, 0); + + return (0); +} From 526ad58e80386abf435e435d34ab2e535c6cf392 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Mon, 6 Aug 2018 03:41:52 +0000 Subject: [PATCH 039/322] bectl(8): Split list functionality out into its own file as well --- sbin/bectl/Makefile | 2 +- sbin/bectl/bectl.c | 379 ------------------------------------ sbin/bectl/bectl.h | 2 + sbin/bectl/bectl_list.c | 418 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 421 insertions(+), 380 deletions(-) create mode 100644 sbin/bectl/bectl_list.c diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index a11ebc4f2072..8da349d24043 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -3,7 +3,7 @@ PROG= bectl MAN= bectl.8 -SRCS= bectl.c bectl_jail.c +SRCS= bectl.c bectl_jail.c bectl_list.c LIBADD+= be LIBADD+= jail diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 8728b0e55c22..405edf5dfb8c 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -43,40 +43,12 @@ #include "bectl.h" -#define HEADER_BE "BE" -#define HEADER_BEPLUS "BE/Dataset/Snapshot" -#define HEADER_ACTIVE "Active" -#define HEADER_MOUNT "Mountpoint" -#define HEADER_SPACE "Space" -#define HEADER_CREATED "Created" - -/* Spaces */ -#define INDENT_INCREMENT 2 - -struct printc { - int active_colsz_def; - int be_colsz; - int current_indent; - int mount_colsz; - int space_colsz; - bool script_fmt; - bool show_all_datasets; - bool show_snaps; - bool show_space; -}; - static int bectl_cmd_activate(int argc, char *argv[]); static int bectl_cmd_create(int argc, char *argv[]); static int bectl_cmd_destroy(int argc, char *argv[]); static int bectl_cmd_export(int argc, char *argv[]); static int bectl_cmd_import(int argc, char *argv[]); static int bectl_cmd_add(int argc, char *argv[]); -static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops); -static void print_padding(const char *fval, int colsz, struct printc *pc); -static int print_snapshots(const char *dsname, struct printc *pc); -static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc); -static void print_headers(nvlist_t *props, struct printc *pc); -static int bectl_cmd_list(int argc, char *argv[]); static int bectl_cmd_mount(int argc, char *argv[]); static int bectl_cmd_rename(int argc, char *argv[]); static int bectl_cmd_unmount(int argc, char *argv[]); @@ -375,357 +347,6 @@ bectl_cmd_destroy(int argc, char *argv[]) return (err); } -/* - * Given a set of dataset properties (for a BE dataset), populate originprops - * with the origin's properties. - */ -static const char * -get_origin_props(nvlist_t *dsprops, nvlist_t **originprops) -{ - char *propstr; - - if (nvlist_lookup_string(dsprops, "origin", &propstr) == 0) { - if (be_prop_list_alloc(originprops) != 0) { - fprintf(stderr, - "bectl list: failed to allocate origin prop nvlist\n"); - return (NULL); - } - if (be_get_dataset_props(be, propstr, *originprops) != 0) { - /* XXX TODO: Real errors */ - fprintf(stderr, - "bectl list: failed to fetch origin properties\n"); - return (NULL); - } - - return (propstr); - } - return (NULL); -} - -static void -print_padding(const char *fval, int colsz, struct printc *pc) -{ - - /* -H flag handling; all delimiters/padding are a single tab */ - if (pc->script_fmt) { - printf("\t"); - return; - } - - if (fval != NULL) - colsz -= strlen(fval); - printf("%*s ", colsz, ""); -} - -static unsigned long long -dataset_space(const char *oname) -{ - unsigned long long space; - char *dsname, *propstr, *sep; - nvlist_t *dsprops; - - space = 0; - dsname = strdup(oname); - if (dsname == NULL) - return (0); - - /* Truncate snapshot to dataset name, as needed */ - if ((sep = strchr(dsname, '@')) != NULL) - *sep = '\0'; - - if (be_prop_list_alloc(&dsprops) != 0) { - free(dsname); - return (0); - } - - if (be_get_dataset_props(be, dsname, dsprops) != 0) { - nvlist_free(dsprops); - free(dsname); - return (0); - } - - if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) - space = strtoull(propstr, NULL, 10); - - nvlist_free(dsprops); - free(dsname); - return (space); -} - -static int -print_snapshots(const char *dsname, struct printc *pc) -{ - nvpair_t *cur; - nvlist_t *props, *sprops; - - if (be_prop_list_alloc(&props) != 0) { - fprintf(stderr, "bectl list: failed to allocate snapshot nvlist\n"); - return (1); - } - if (be_get_dataset_snapshots(be, dsname, props) != 0) { - fprintf(stderr, "bectl list: failed to fetch boot ds snapshots\n"); - return (1); - } - for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; - cur = nvlist_next_nvpair(props, cur)) { - nvpair_value_nvlist(cur, &sprops); - print_info(nvpair_name(cur), sprops, pc); - } - return (0); -} - -static void -print_info(const char *name, nvlist_t *dsprops, struct printc *pc) -{ -#define BUFSZ 64 - char buf[BUFSZ]; - unsigned long long ctimenum, space; - nvlist_t *originprops; - const char *oname; - char *dsname, *propstr; - int active_colsz; - boolean_t active_now, active_reboot; - - dsname = NULL; - originprops = NULL; - printf("%*s%s", pc->current_indent, "", name); - nvlist_lookup_string(dsprops, "dataset", &dsname); - - /* Recurse at the base level if we're breaking info down */ - if (pc->current_indent == 0 && (pc->show_all_datasets || - pc->show_snaps)) { - printf("\n"); - if (dsname == NULL) - /* XXX TODO: Error? */ - return; - /* - * Whether we're dealing with -a or -s, we'll always print the - * dataset name/information followed by its origin. For -s, we - * additionally iterate through all snapshots of this boot - * environment and also print their information. - */ - pc->current_indent += INDENT_INCREMENT; - print_info(dsname, dsprops, pc); - pc->current_indent += INDENT_INCREMENT; - if ((oname = get_origin_props(dsprops, &originprops)) != NULL) { - print_info(oname, originprops, pc); - nvlist_free(originprops); - } - - /* Back up a level; snapshots at the same level as dataset */ - pc->current_indent -= INDENT_INCREMENT; - if (pc->show_snaps) - print_snapshots(dsname, pc); - pc->current_indent = 0; - return; - } else - print_padding(name, pc->be_colsz - pc->current_indent, pc); - - active_colsz = pc->active_colsz_def; - if (nvlist_lookup_boolean_value(dsprops, "active", - &active_now) == 0 && active_now) { - printf("N"); - active_colsz--; - } - if (nvlist_lookup_boolean_value(dsprops, "nextboot", - &active_reboot) == 0 && active_reboot) { - printf("R"); - active_colsz--; - } - if (active_colsz == pc->active_colsz_def) { - printf("-"); - active_colsz--; - } - print_padding(NULL, active_colsz, pc); - if (nvlist_lookup_string(dsprops, "mountpoint", &propstr) == 0){ - printf("%s", propstr); - print_padding(propstr, pc->mount_colsz, pc); - } else { - printf("%s", "-"); - print_padding("-", pc->mount_colsz, pc); - } - - oname = get_origin_props(dsprops, &originprops); - if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { - /* - * The space used column is some composition of: - * - The "used" property of the dataset - * - The "used" property of the origin snapshot (not -a or -s) - * - The "used" property of the origin dataset (-D flag only) - * - * The -D flag is ignored if -a or -s are specified. - */ - space = strtoull(propstr, NULL, 10); - - if (!pc->show_all_datasets && !pc->show_snaps && - originprops != NULL && - nvlist_lookup_string(originprops, "used", &propstr) == 0) - space += strtoull(propstr, NULL, 10); - - if (pc->show_space && oname != NULL) - space += dataset_space(oname); - - /* Alas, there's more to it,. */ - be_nicenum(space, buf, 6); - printf("%s", buf); - print_padding(buf, pc->space_colsz, pc); - } else { - printf("-"); - print_padding("-", pc->space_colsz, pc); - } - - if (nvlist_lookup_string(dsprops, "creation", &propstr) == 0) { - ctimenum = strtoull(propstr, NULL, 10); - strftime(buf, BUFSZ, "%Y-%m-%d %H:%M", - localtime((time_t *)&ctimenum)); - printf("%s", buf); - } - - printf("\n"); - if (originprops != NULL) - be_prop_list_free(originprops); -#undef BUFSZ -} - -static void -print_headers(nvlist_t *props, struct printc *pc) -{ - const char *chosen_be_header; - nvpair_t *cur; - nvlist_t *dsprops; - char *propstr; - size_t be_maxcol; - - if (pc->show_all_datasets || pc->show_snaps) - chosen_be_header = HEADER_BEPLUS; - else - chosen_be_header = HEADER_BE; - be_maxcol = strlen(chosen_be_header); - for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; - cur = nvlist_next_nvpair(props, cur)) { - be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur))); - if (!pc->show_all_datasets && !pc->show_snaps) - continue; - nvpair_value_nvlist(cur, &dsprops); - if (nvlist_lookup_string(dsprops, "dataset", &propstr) != 0) - continue; - be_maxcol = MAX(be_maxcol, strlen(propstr) + INDENT_INCREMENT); - if (nvlist_lookup_string(dsprops, "origin", &propstr) != 0) - continue; - be_maxcol = MAX(be_maxcol, - strlen(propstr) + INDENT_INCREMENT * 2); - } - - pc->be_colsz = be_maxcol; - pc->active_colsz_def = strlen(HEADER_ACTIVE); - pc->mount_colsz = strlen(HEADER_MOUNT); - pc->space_colsz = strlen(HEADER_SPACE); - printf("%*s %s %s %s %s\n", -pc->be_colsz, chosen_be_header, - HEADER_ACTIVE, HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); - - /* - * All other invocations in which we aren't using the default header - * will produce quite a bit of input. Throw an extra blank line after - * the header to make it look nicer. - */ - if (chosen_be_header != HEADER_BE) - printf("\n"); -} - -static int -bectl_cmd_list(int argc, char *argv[]) -{ - struct printc pc; - nvpair_t *cur; - nvlist_t *dsprops, *props; - int opt, printed; - boolean_t active_now, active_reboot; - - props = NULL; - printed = 0; - bzero(&pc, sizeof(pc)); - while ((opt = getopt(argc, argv, "aDHs")) != -1) { - switch (opt) { - case 'a': - pc.show_all_datasets = true; - break; - case 'D': - pc.show_space = true; - break; - case 'H': - pc.script_fmt = true; - break; - case 's': - pc.show_snaps = true; - break; - default: - fprintf(stderr, "bectl list: unknown option '-%c'\n", - optopt); - return (usage(false)); - } - } - - argc -= optind; - - if (argc != 0) { - fprintf(stderr, "bectl list: extra argument provided\n"); - return (usage(false)); - } - - if (be_prop_list_alloc(&props) != 0) { - fprintf(stderr, "bectl list: failed to allocate prop nvlist\n"); - return (1); - } - if (be_get_bootenv_props(be, props) != 0) { - /* XXX TODO: Real errors */ - fprintf(stderr, "bectl list: failed to fetch boot environments\n"); - return (1); - } - - /* Force -D off if either -a or -s are specified */ - if (pc.show_all_datasets || pc.show_snaps) - pc.show_space = false; - if (!pc.script_fmt) - print_headers(props, &pc); - /* Do a first pass to print active and next active first */ - for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; - cur = nvlist_next_nvpair(props, cur)) { - nvpair_value_nvlist(cur, &dsprops); - active_now = active_reboot = false; - - nvlist_lookup_boolean_value(dsprops, "active", &active_now); - nvlist_lookup_boolean_value(dsprops, "nextboot", - &active_reboot); - if (!active_now && !active_reboot) - continue; - if (printed > 0 && (pc.show_all_datasets || pc.show_snaps)) - printf("\n"); - print_info(nvpair_name(cur), dsprops, &pc); - printed++; - } - - /* Now pull everything else */ - for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; - cur = nvlist_next_nvpair(props, cur)) { - nvpair_value_nvlist(cur, &dsprops); - active_now = active_reboot = false; - - nvlist_lookup_boolean_value(dsprops, "active", &active_now); - nvlist_lookup_boolean_value(dsprops, "nextboot", - &active_reboot); - if (active_now || active_reboot) - continue; - if (printed > 0 && (pc.show_all_datasets || pc.show_snaps)) - printf("\n"); - print_info(nvpair_name(cur), dsprops, &pc); - printed++; - } - be_prop_list_free(props); - - return (0); -} - - static int bectl_cmd_mount(int argc, char *argv[]) { diff --git a/sbin/bectl/bectl.h b/sbin/bectl/bectl.h index 3b8abb8aaaad..878af24f0cbb 100644 --- a/sbin/bectl/bectl.h +++ b/sbin/bectl/bectl.h @@ -32,4 +32,6 @@ int usage(bool explicit); int bectl_cmd_jail(int argc, char *argv[]); int bectl_cmd_unjail(int argc, char *argv[]); +int bectl_cmd_list(int argc, char *argv[]); + extern libbe_handle_t *be; diff --git a/sbin/bectl/bectl_list.c b/sbin/bectl/bectl_list.c new file mode 100644 index 000000000000..f0cc4d60f5e8 --- /dev/null +++ b/sbin/bectl/bectl_list.c @@ -0,0 +1,418 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Kyle Evans + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include + +#include "bectl.h" + +struct printc { + int active_colsz_def; + int be_colsz; + int current_indent; + int mount_colsz; + int space_colsz; + bool script_fmt; + bool show_all_datasets; + bool show_snaps; + bool show_space; +}; + +static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops); +static void print_padding(const char *fval, int colsz, struct printc *pc); +static int print_snapshots(const char *dsname, struct printc *pc); +static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc); +static void print_headers(nvlist_t *props, struct printc *pc); +static unsigned long long dataset_space(const char *oname); + +#define HEADER_BE "BE" +#define HEADER_BEPLUS "BE/Dataset/Snapshot" +#define HEADER_ACTIVE "Active" +#define HEADER_MOUNT "Mountpoint" +#define HEADER_SPACE "Space" +#define HEADER_CREATED "Created" + +/* Spaces */ +#define INDENT_INCREMENT 2 + +/* + * Given a set of dataset properties (for a BE dataset), populate originprops + * with the origin's properties. + */ +static const char * +get_origin_props(nvlist_t *dsprops, nvlist_t **originprops) +{ + char *propstr; + + if (nvlist_lookup_string(dsprops, "origin", &propstr) == 0) { + if (be_prop_list_alloc(originprops) != 0) { + fprintf(stderr, + "bectl list: failed to allocate origin prop nvlist\n"); + return (NULL); + } + if (be_get_dataset_props(be, propstr, *originprops) != 0) { + /* XXX TODO: Real errors */ + fprintf(stderr, + "bectl list: failed to fetch origin properties\n"); + return (NULL); + } + + return (propstr); + } + return (NULL); +} + +static void +print_padding(const char *fval, int colsz, struct printc *pc) +{ + + /* -H flag handling; all delimiters/padding are a single tab */ + if (pc->script_fmt) { + printf("\t"); + return; + } + + if (fval != NULL) + colsz -= strlen(fval); + printf("%*s ", colsz, ""); +} + +static unsigned long long +dataset_space(const char *oname) +{ + unsigned long long space; + char *dsname, *propstr, *sep; + nvlist_t *dsprops; + + space = 0; + dsname = strdup(oname); + if (dsname == NULL) + return (0); + + /* Truncate snapshot to dataset name, as needed */ + if ((sep = strchr(dsname, '@')) != NULL) + *sep = '\0'; + + if (be_prop_list_alloc(&dsprops) != 0) { + free(dsname); + return (0); + } + + if (be_get_dataset_props(be, dsname, dsprops) != 0) { + nvlist_free(dsprops); + free(dsname); + return (0); + } + + if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) + space = strtoull(propstr, NULL, 10); + + nvlist_free(dsprops); + free(dsname); + return (space); +} + +static int +print_snapshots(const char *dsname, struct printc *pc) +{ + nvpair_t *cur; + nvlist_t *props, *sprops; + + if (be_prop_list_alloc(&props) != 0) { + fprintf(stderr, "bectl list: failed to allocate snapshot nvlist\n"); + return (1); + } + if (be_get_dataset_snapshots(be, dsname, props) != 0) { + fprintf(stderr, "bectl list: failed to fetch boot ds snapshots\n"); + return (1); + } + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + nvpair_value_nvlist(cur, &sprops); + print_info(nvpair_name(cur), sprops, pc); + } + return (0); +} + +static void +print_info(const char *name, nvlist_t *dsprops, struct printc *pc) +{ +#define BUFSZ 64 + char buf[BUFSZ]; + unsigned long long ctimenum, space; + nvlist_t *originprops; + const char *oname; + char *dsname, *propstr; + int active_colsz; + boolean_t active_now, active_reboot; + + dsname = NULL; + originprops = NULL; + printf("%*s%s", pc->current_indent, "", name); + nvlist_lookup_string(dsprops, "dataset", &dsname); + + /* Recurse at the base level if we're breaking info down */ + if (pc->current_indent == 0 && (pc->show_all_datasets || + pc->show_snaps)) { + printf("\n"); + if (dsname == NULL) + /* XXX TODO: Error? */ + return; + /* + * Whether we're dealing with -a or -s, we'll always print the + * dataset name/information followed by its origin. For -s, we + * additionally iterate through all snapshots of this boot + * environment and also print their information. + */ + pc->current_indent += INDENT_INCREMENT; + print_info(dsname, dsprops, pc); + pc->current_indent += INDENT_INCREMENT; + if ((oname = get_origin_props(dsprops, &originprops)) != NULL) { + print_info(oname, originprops, pc); + nvlist_free(originprops); + } + + /* Back up a level; snapshots at the same level as dataset */ + pc->current_indent -= INDENT_INCREMENT; + if (pc->show_snaps) + print_snapshots(dsname, pc); + pc->current_indent = 0; + return; + } else + print_padding(name, pc->be_colsz - pc->current_indent, pc); + + active_colsz = pc->active_colsz_def; + if (nvlist_lookup_boolean_value(dsprops, "active", + &active_now) == 0 && active_now) { + printf("N"); + active_colsz--; + } + if (nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot) == 0 && active_reboot) { + printf("R"); + active_colsz--; + } + if (active_colsz == pc->active_colsz_def) { + printf("-"); + active_colsz--; + } + print_padding(NULL, active_colsz, pc); + if (nvlist_lookup_string(dsprops, "mountpoint", &propstr) == 0){ + printf("%s", propstr); + print_padding(propstr, pc->mount_colsz, pc); + } else { + printf("%s", "-"); + print_padding("-", pc->mount_colsz, pc); + } + + oname = get_origin_props(dsprops, &originprops); + if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) { + /* + * The space used column is some composition of: + * - The "used" property of the dataset + * - The "used" property of the origin snapshot (not -a or -s) + * - The "used" property of the origin dataset (-D flag only) + * + * The -D flag is ignored if -a or -s are specified. + */ + space = strtoull(propstr, NULL, 10); + + if (!pc->show_all_datasets && !pc->show_snaps && + originprops != NULL && + nvlist_lookup_string(originprops, "used", &propstr) == 0) + space += strtoull(propstr, NULL, 10); + + if (pc->show_space && oname != NULL) + space += dataset_space(oname); + + /* Alas, there's more to it,. */ + be_nicenum(space, buf, 6); + printf("%s", buf); + print_padding(buf, pc->space_colsz, pc); + } else { + printf("-"); + print_padding("-", pc->space_colsz, pc); + } + + if (nvlist_lookup_string(dsprops, "creation", &propstr) == 0) { + ctimenum = strtoull(propstr, NULL, 10); + strftime(buf, BUFSZ, "%Y-%m-%d %H:%M", + localtime((time_t *)&ctimenum)); + printf("%s", buf); + } + + printf("\n"); + if (originprops != NULL) + be_prop_list_free(originprops); +#undef BUFSZ +} + +static void +print_headers(nvlist_t *props, struct printc *pc) +{ + const char *chosen_be_header; + nvpair_t *cur; + nvlist_t *dsprops; + char *propstr; + size_t be_maxcol; + + if (pc->show_all_datasets || pc->show_snaps) + chosen_be_header = HEADER_BEPLUS; + else + chosen_be_header = HEADER_BE; + be_maxcol = strlen(chosen_be_header); + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur))); + if (!pc->show_all_datasets && !pc->show_snaps) + continue; + nvpair_value_nvlist(cur, &dsprops); + if (nvlist_lookup_string(dsprops, "dataset", &propstr) != 0) + continue; + be_maxcol = MAX(be_maxcol, strlen(propstr) + INDENT_INCREMENT); + if (nvlist_lookup_string(dsprops, "origin", &propstr) != 0) + continue; + be_maxcol = MAX(be_maxcol, + strlen(propstr) + INDENT_INCREMENT * 2); + } + + pc->be_colsz = be_maxcol; + pc->active_colsz_def = strlen(HEADER_ACTIVE); + pc->mount_colsz = strlen(HEADER_MOUNT); + pc->space_colsz = strlen(HEADER_SPACE); + printf("%*s %s %s %s %s\n", -pc->be_colsz, chosen_be_header, + HEADER_ACTIVE, HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED); + + /* + * All other invocations in which we aren't using the default header + * will produce quite a bit of input. Throw an extra blank line after + * the header to make it look nicer. + */ + if (chosen_be_header != HEADER_BE) + printf("\n"); +} + +int +bectl_cmd_list(int argc, char *argv[]) +{ + struct printc pc; + nvpair_t *cur; + nvlist_t *dsprops, *props; + int opt, printed; + boolean_t active_now, active_reboot; + + props = NULL; + printed = 0; + bzero(&pc, sizeof(pc)); + while ((opt = getopt(argc, argv, "aDHs")) != -1) { + switch (opt) { + case 'a': + pc.show_all_datasets = true; + break; + case 'D': + pc.show_space = true; + break; + case 'H': + pc.script_fmt = true; + break; + case 's': + pc.show_snaps = true; + break; + default: + fprintf(stderr, "bectl list: unknown option '-%c'\n", + optopt); + return (usage(false)); + } + } + + argc -= optind; + + if (argc != 0) { + fprintf(stderr, "bectl list: extra argument provided\n"); + return (usage(false)); + } + + if (be_prop_list_alloc(&props) != 0) { + fprintf(stderr, "bectl list: failed to allocate prop nvlist\n"); + return (1); + } + if (be_get_bootenv_props(be, props) != 0) { + /* XXX TODO: Real errors */ + fprintf(stderr, "bectl list: failed to fetch boot environments\n"); + return (1); + } + + /* Force -D off if either -a or -s are specified */ + if (pc.show_all_datasets || pc.show_snaps) + pc.show_space = false; + if (!pc.script_fmt) + print_headers(props, &pc); + /* Do a first pass to print active and next active first */ + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + nvpair_value_nvlist(cur, &dsprops); + active_now = active_reboot = false; + + nvlist_lookup_boolean_value(dsprops, "active", &active_now); + nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot); + if (!active_now && !active_reboot) + continue; + if (printed > 0 && (pc.show_all_datasets || pc.show_snaps)) + printf("\n"); + print_info(nvpair_name(cur), dsprops, &pc); + printed++; + } + + /* Now pull everything else */ + for (cur = nvlist_next_nvpair(props, NULL); cur != NULL; + cur = nvlist_next_nvpair(props, cur)) { + nvpair_value_nvlist(cur, &dsprops); + active_now = active_reboot = false; + + nvlist_lookup_boolean_value(dsprops, "active", &active_now); + nvlist_lookup_boolean_value(dsprops, "nextboot", + &active_reboot); + if (active_now || active_reboot) + continue; + if (printed > 0 && (pc.show_all_datasets || pc.show_snaps)) + printf("\n"); + print_info(nvpair_name(cur), dsprops, &pc); + printed++; + } + be_prop_list_free(props); + + return (0); +} + From 2a0b8dc2cb22e39d566e30c03ed437d494f99821 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Mon, 6 Aug 2018 15:21:46 +0000 Subject: [PATCH 040/322] bectl(8): Provide -u option to unset jail parameters All but name, host.hostname, and path may be completely unset. --- sbin/bectl/bectl.8 | 26 +++++++++++++++---- sbin/bectl/bectl.c | 2 +- sbin/bectl/bectl_jail.c | 57 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index a55e9af8aaf5..22baa0574c0b 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -40,7 +40,7 @@ destroy .Ao Ar beName | beName@snapshot Ac .Nm jail -.Op Fl o Ar key Ns = Ns Ar value Oc Ns ... +.Oo Fl o Ar key Ns = Ns Ar value | Fl u Ar key Oc Ns ... .Ao Ar jailID | jailName Ac .Ao Ar bootenv Ac .Nm @@ -124,15 +124,32 @@ Specifying will automatically unmount without confirmation. .Pp .It Ic jail -.Op Fl o Ar key Ns = Ns Ar value Oc Ns ... +.Oo Fl o Ar key Ns = Ns Ar value | Fl u Ar key Oc Ns ... .Ao Ar jailID | jailName Ac .Ao Ar bootenv Ac .Pp Creates a jail of the given boot environment. Multiple .Fl o +and +.Fl u arguments may be specified. -Al +.Fl o +will set a jail parameter, and +.Fl u +will unset a jail parameter. +.Pp +The +.Va name , +.Va host.hostname , +and +.Va path +may not actually be unset. +Attempts to unset any of these will revert them to the default values specified +below, if they have been overwritten by +.Fl o . +.Pp +All .Ar key , .Ar value pairs are interpreted as jail parameters as described in @@ -148,9 +165,8 @@ The following default parameters are provided: Set to a path in /tmp generated by .Xr libbe 8 . .El -.pp -All default parameters may be overwritten. .Pp +All default parameters may be overwritten. .It Ic list .Op Fl a .Op Fl D diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 405edf5dfb8c..da01840ed196 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -70,7 +70,7 @@ usage(bool explicit) "\tbectl export sourceBe\n" "\tbectl import targetBe\n" "\tbectl add (path)*\n" - "\tbectl jail [ -o key=value ]... bootenv\n" + "\tbectl jail [ -o key=value | -u key ]... bootenv\n" "\tbectl list [-a] [-D] [-H] [-s]\n" "\tbectl mount beName [mountpoint]\n" "\tbectl rename origBeName newBeName\n" diff --git a/sbin/bectl/bectl_jail.c b/sbin/bectl/bectl_jail.c index b012a906c5e2..1731af43514d 100644 --- a/sbin/bectl/bectl_jail.c +++ b/sbin/bectl/bectl_jail.c @@ -43,7 +43,9 @@ static void jailparam_grow(void); static void jailparam_add(const char *name, const char *val); +static void jailparam_del(const char *name); static bool jailparam_addarg(char *arg); +static bool jailparam_delarg(char *arg); static int bectl_search_jail_paths(const char *mnt); static int bectl_locate_jail(const char *ident); @@ -89,6 +91,34 @@ jailparam_add(const char *name, const char *val) ++jpused; } +static void +jailparam_del(const char *name) +{ + int i; + char *val; + + for (i = 0; i < jpused; ++i) { + if (strcmp(name, jp[i].jp_name) == 0) + break; + } + + /* Not found... technically successful */ + if (i == jpused) + return; + + for (; i < jpused - 1; ++i) { + val = jailparam_export(&jp[i + 1]); + + jailparam_free(&jp[i], 1); + jailparam_init(&jp[i], jp[i + 1].jp_name); + jailparam_import(&jp[i], val); + free(val); + } + + jailparam_free(&jp[i], 1); + --jpused; +} + static bool jailparam_addarg(char *arg) { @@ -117,6 +147,23 @@ jailparam_addarg(char *arg) return (true); } +static bool +jailparam_delarg(char *arg) +{ + char *name, *val; + + if (arg == NULL) + return (false); + name = arg; + if ((val = strchr(name, '=')) != NULL) + *val++ = '\0'; + + if (strcmp(name, "path") == 0) + *mnt_loc = '\0'; + jailparam_del(name); + return (true); +} + int bectl_cmd_jail(int argc, char *argv[]) { @@ -135,7 +182,7 @@ bectl_cmd_jail(int argc, char *argv[]) jailparam_add("allow.mount.devfs", "true"); jailparam_add("enforce_statfs", "1"); - while ((opt = getopt(argc, argv, "o:")) != -1) { + while ((opt = getopt(argc, argv, "o:u:")) != -1) { switch (opt) { case 'o': if (jailparam_addarg(optarg)) { @@ -149,6 +196,14 @@ bectl_cmd_jail(int argc, char *argv[]) default_hostname = false; } break; + case 'u': + if (jailparam_delarg(optarg)) { + if (strcmp(optarg, "name") == 0) + default_name = true; + if (strcmp(optarg, "host.hostname") == 0) + default_hostname = true; + } + break; default: fprintf(stderr, "bectl jail: unknown option '-%c'\n", optopt); From d06f71037bda98781494b10cf0ce070410ddbda6 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 01:56:37 +0000 Subject: [PATCH 041/322] libbe(3): Rewrite activate temp bits to rely less on loader Loader is still relied upon at the beginning of libbe to specify the be root, but we can derive from that the primary zpool and any vdevs that we need to set nextboot bits on. This lets me successfully `bectl activate -t test`, but UEFI loader doesn't quite yet understand so it's effectively defunct. --- lib/libbe/be.c | 59 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 4e889abee2e4..7493b1dc28ab 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -657,7 +657,7 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) * XXX TODO: this is a very likely name for someone to already have * used... we should avoid it. */ - if ((err = be_root_concat(lbh, "be_import_temp", buf)) != 0) + if ((err = be_root_concat(lbh, "libbe_import_temp", buf)) != 0) /* XXX TODO error handle */ return (-1); @@ -798,6 +798,35 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) return (BE_ERR_SUCCESS); } +static int +be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, + const char *zfsdev) +{ + nvlist_t **child; + uint64_t vdev_guid; + int c, children; + + if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, + &children) == 0) { + for (c = 0; c < children; ++c) + if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) + return (1); + return (0); + } + + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, + &vdev_guid) != 0) { + return (1); + } + + if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { + perror("ZFS_IOC_NEXTBOOT failed"); + return (1); + } + + return (0); +} + int be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) @@ -805,7 +834,7 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) char be_path[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; uint64_t pool_guid; - uint64_t vdev_guid; + nvlist_t *config, *vdevs; int err; be_root_concat(lbh, bootenv, be_path); @@ -819,33 +848,25 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) * XXX TODO: give proper attribution to author(s) of zfsbootcfg * for this snippet. */ - - if (kenv(KENV_GET, "vfs.zfs.boot.primary_pool", buf, - sizeof(buf)) <= 0) - return (1); - pool_guid = strtoumax(buf, NULL, 10); - if (pool_guid == 0) - return (1); - - if (kenv(KENV_GET, "vfs.zfs.boot.primary_vdev", buf, - sizeof(buf)) <= 0) - return (1); - vdev_guid = strtoumax(buf, NULL, 10); - if (vdev_guid == 0) { + config = zpool_get_config(lbh->active_phandle, NULL); + if (config == NULL) { + printf("no config\n"); return (1); } + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &pool_guid) != 0) + return (1); + /* Expected format according to zfsbootcfg(8) man */ strcpy(buf, "zfs:"); strcat(buf, be_path); strcat(buf, ":"); - if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, buf) != 0) { - perror("ZFS_IOC_NEXTBOOT failed"); + if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdevs) != 0) return (1); - } - return (BE_ERR_SUCCESS); + return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); } else { /* Obtain bootenv zpool */ err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); From 3f4e6c966c306cb9f983f8d23b658728156ee30e Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 02:32:29 +0000 Subject: [PATCH 042/322] This snippet is no longer from zfsbootcfg --- lib/libbe/be.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 7493b1dc28ab..0d6ccf28b6ad 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -844,10 +844,6 @@ be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) return (BE_ERR_NOENT); if (temporary) { - /* - * XXX TODO: give proper attribution to author(s) of zfsbootcfg - * for this snippet. - */ config = zpool_get_config(lbh->active_phandle, NULL); if (config == NULL) { printf("no config\n"); From 5468566e2df061adeac58193852e3c5b177a5814 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 02:40:00 +0000 Subject: [PATCH 043/322] libbe(3): Rename prop_list_builder and snapshot_prop_list_builder --- lib/libbe/be_impl.h | 2 +- lib/libbe/be_info.c | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index 89894f4ac026..0b40a285b6e6 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -62,7 +62,7 @@ typedef struct prop_data { } prop_data_t; int prop_list_builder_cb(zfs_handle_t *, void *); -int prop_list_builder(prop_data_t *); +int be_proplist_update(prop_data_t *); int set_error(libbe_handle_t *, be_error_t); diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 1a12b2d58994..fa7c2254d724 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -29,7 +29,7 @@ #include "be.h" #include "be_impl.h" -static int snapshot_prop_list_builder(zfs_handle_t *hdl, prop_data_t *data); +static int snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data); /* * Returns the name of the active boot environment @@ -97,7 +97,7 @@ be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) data.lbh = lbh; data.list = dsnvl; data.single_object = false; - return (prop_list_builder(&data)); + return (be_proplist_update(&data)); } int @@ -133,7 +133,7 @@ be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props) ZFS_TYPE_FILESYSTEM)) == NULL) return (BE_ERR_ZFSOPEN); - ret = snapshot_prop_list_builder(ds_hdl, &data); + ret = snapshot_proplist_update(ds_hdl, &data); zfs_close(ds_hdl); return (ret); } @@ -141,7 +141,6 @@ be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props) /* * Internal callback function used by zfs_iter_filesystems. For each dataset in * the bootenv root, populate an nvlist_t of its relevant properties. - * TODO: should any other properties be included? */ int prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) @@ -221,12 +220,11 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) /* * Updates the properties of each bootenv in the libbe handle - * XXX TODO: rename to be_proplist_update * XXX TODO: ensure that this is always consistent (run after adds, deletes, * renames,etc */ int -prop_list_builder(prop_data_t *data) +be_proplist_update(prop_data_t *data) { zfs_handle_t *root_hdl; @@ -243,7 +241,7 @@ prop_list_builder(prop_data_t *data) } static int -snapshot_prop_list_builder(zfs_handle_t *hdl, prop_data_t *data) +snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data) { return (zfs_iter_snapshots_sorted(hdl, prop_list_builder_cb, data)); From 9c65c7fb48ca4907091fe75075e7fbdf11ed9840 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 03:01:04 +0000 Subject: [PATCH 044/322] bectl(8): Only show mountpoint if the dataset is actually mounted This is to accomodate a later change in libbe(3) that will always return the mountpoint, whether it be the directory the dataset is actively mounted at or the "mountpoint" property. --- sbin/bectl/bectl_list.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sbin/bectl/bectl_list.c b/sbin/bectl/bectl_list.c index f0cc4d60f5e8..bb98093b1d60 100644 --- a/sbin/bectl/bectl_list.c +++ b/sbin/bectl/bectl_list.c @@ -175,7 +175,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) const char *oname; char *dsname, *propstr; int active_colsz; - boolean_t active_now, active_reboot; + boolean_t active_now, active_reboot, mounted; dsname = NULL; originprops = NULL; @@ -228,7 +228,10 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) active_colsz--; } print_padding(NULL, active_colsz, pc); - if (nvlist_lookup_string(dsprops, "mountpoint", &propstr) == 0){ + if (nvlist_lookup_boolean_value(dsprops, "mounted", &mounted) != 0) + mounted = false; + if (mounted && nvlist_lookup_string(dsprops, "mountpoint", + &propstr) == 0) { printf("%s", propstr); print_padding(propstr, pc->mount_colsz, pc); } else { From 709b553cd0308060a548c1aa9c6ff026b1dcdd1e Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 03:07:54 +0000 Subject: [PATCH 045/322] libbe(3): Check that dataset is to be mounted at / for be_exists This makes the be_exists behavior match the comments that assert that we've already checked that the dataset derived from the BE name is set to mount at /. Other changes of note: - bectl_list sees another change; changing mountpoint based on mount status turns out to be a bad idea, so instead make the mounted property of the returned nvlist the path that it's mounted at - Always return the "mountpoint" property in "mountpoint" if it's ste --- lib/libbe/be_info.c | 37 ++++++++++++++++++++++++++++++------- sbin/bectl/bectl_list.c | 7 ++----- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index fa7c2254d724..04bbd71c4f7b 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -172,10 +172,13 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) nvlist_add_string(props, "name", name); mounted = zfs_is_mounted(zfs_hdl, &mountpoint); - nvlist_add_boolean_value(props, "mounted", mounted); if (mounted) - nvlist_add_string(props, "mountpoint", mountpoint); + nvlist_add_string(props, "mounted", mountpoint); + + if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512, + NULL, NULL, 0, 1) == 0) + nvlist_add_string(props, "mountpoint", buf); if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, NULL, NULL, 0, 1) == 0) @@ -282,12 +285,32 @@ bool be_exists(libbe_handle_t *lbh, char *be) { char buf[BE_MAXPATHLEN]; + nvlist_t *dsprops; + char *mntpoint; + bool valid; be_root_concat(lbh, be, buf); - /* - * XXX TODO: check mountpoint prop and see if its /, AND that result - * with below expression. - */ - return (zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET)); + if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET)) + return (false); + + /* Also check if it's mounted at / */ + if (be_prop_list_alloc(&dsprops) != 0) { + set_error(lbh, BE_ERR_UNKNOWN); + return (false); + } + + if (be_get_dataset_props(lbh, buf, dsprops) != 0) { + nvlist_free(dsprops); + return (false); + } + + if (nvlist_lookup_string(dsprops, "mountpoint", &mntpoint) == 0) { + valid = (strcmp(mntpoint, "/") == 0); + nvlist_free(dsprops); + return (valid); + } + + nvlist_free(dsprops); + return (false); } diff --git a/sbin/bectl/bectl_list.c b/sbin/bectl/bectl_list.c index bb98093b1d60..cbdac3786cc9 100644 --- a/sbin/bectl/bectl_list.c +++ b/sbin/bectl/bectl_list.c @@ -175,7 +175,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) const char *oname; char *dsname, *propstr; int active_colsz; - boolean_t active_now, active_reboot, mounted; + boolean_t active_now, active_reboot; dsname = NULL; originprops = NULL; @@ -228,10 +228,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc) active_colsz--; } print_padding(NULL, active_colsz, pc); - if (nvlist_lookup_boolean_value(dsprops, "mounted", &mounted) != 0) - mounted = false; - if (mounted && nvlist_lookup_string(dsprops, "mountpoint", - &propstr) == 0) { + if (nvlist_lookup_string(dsprops, "mounted", &propstr) == 0) { printf("%s", propstr); print_padding(propstr, pc->mount_colsz, pc); } else { From f1ca70d3e1d320fe687348e8a8394b1ea571f279 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 03:25:28 +0000 Subject: [PATCH 046/322] libbe(3): Return some more proper error codes --- lib/libbe/be.h | 3 +++ lib/libbe/be_access.c | 50 ++++++++++++++++++++++++++++++++----------- lib/libbe/libbe.3 | 5 ++++- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/lib/libbe/be.h b/lib/libbe/be.h index a4d0f3954406..dea934af1dad 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -44,6 +44,8 @@ typedef enum be_error { BE_ERR_PERMS, /* insufficient permissions */ BE_ERR_DESTROYACT, /* cannot destroy active boot env */ BE_ERR_DESTROYMNT, /* destroying a mounted be requires force */ + 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_NOORIGIN, /* could not open snapshot's origin */ @@ -51,6 +53,7 @@ typedef enum be_error { BE_ERR_NOMOUNT, /* boot environment is not mounted */ BE_ERR_ZFSOPEN, /* calling zfs_open() failed */ BE_ERR_ZFSCLONE, /* error when calling zfs_clone to create be */ + BE_ERR_IO, /* error when doing some I/O operation */ BE_ERR_UNKNOWN, /* unknown error */ } be_error_t; diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index 55481e04e14b..c0acac215eb7 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -121,18 +121,27 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, if (mountpoint == NULL) { strcpy(mnt_temp, "/tmp/be_mount.XXXX"); if (mkdtemp(mnt_temp) == NULL) - /* XXX TODO: create error for this */ - return (set_error(lbh, BE_ERR_UNKNOWN)); + return (set_error(lbh, BE_ERR_IO)); } char opt = '\0'; if ((err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint, - mntflags, __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) - /* - * XXX TODO: zmount returns the nmount error, look into what - * kind of errors we can report from that - */ - return (set_error(lbh, BE_ERR_UNKNOWN)); + mntflags, __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) { + switch (errno) { + case ENAMETOOLONG: + return (set_error(lbh, BE_ERR_PATHLEN)); + case ELOOP: + case ENOENT: + case ENOTDIR: + return (set_error(lbh, BE_ERR_BADPATH)); + case EPERM: + return (set_error(lbh, BE_ERR_PERMS)); + case EBUSY: + return (set_error(lbh, BE_ERR_PATHBUSY)); + default: + return (set_error(lbh, BE_ERR_UNKNOWN)); + } + } if (result_loc != NULL) strcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint); @@ -156,9 +165,11 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) if ((err = be_root_concat(lbh, bootenv, be)) != 0) return (set_error(lbh, err)); - if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) - /* XXX TODO correct error */ + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + if (errno == EIO) + return (set_error(lbh, BE_ERR_IO)); return (set_error(lbh, BE_ERR_NOMOUNT)); + } mntpath = NULL; for (int i = 0; i < mntsize; ++i) { @@ -177,9 +188,22 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; - if ((err = unmount(mntpath, mntflags)) != 0) - /* XXX TODO correct error */ - return (set_error(lbh, BE_ERR_NOMOUNT)); + if ((err = unmount(mntpath, mntflags)) != 0) { + switch (errno) { + case ENAMETOOLONG: + return (set_error(lbh, BE_ERR_PATHLEN)); + case ELOOP: + case ENOENT: + case ENOTDIR: + return (set_error(lbh, BE_ERR_BADPATH)); + case EPERM: + return (set_error(lbh, BE_ERR_PERMS)); + case EBUSY: + return (set_error(lbh, BE_ERR_PATHBUSY)); + default: + return (set_error(lbh, BE_ERR_UNKNOWN)); + } + } return (set_error(lbh, BE_ERR_SUCCESS)); } diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index b6cf5a460112..be026a30e754 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -30,7 +30,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 25, 2018 +.Dd August 6, 2018 .Dt LIBBE 3 .Os .Sh NAME @@ -190,6 +190,8 @@ BE_ERR_NOENT, BE_ERR_PERMS, BE_ERR_DESTROYACT, BE_ERR_DESTROYMNT, +BE_ERR_BADPATH, +BE_ERR_PATHBUSY, BE_ERR_PATHLEN, BE_ERR_INVORIGIN, BE_ERR_NOORIGIN, @@ -197,6 +199,7 @@ BE_ERR_MOUNTED, BE_ERR_NOMOUNT, BE_ERR_ZFSOPEN, BE_ERR_ZFSCLONE, +BE_ERR_IO, BE_ERR_UNKNOWN .Ed .Sh SEE ALSO From 20b7b8d86cc142b839c661557553965984818e70 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 03:26:32 +0000 Subject: [PATCH 047/322] libbe(3): Document that we'll clobber previous errors set by set_error --- lib/libbe/be_error.c | 1 - lib/libbe/be_impl.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c index 9da17eb492bd..58df17ba4e20 100644 --- a/lib/libbe/be_error.c +++ b/lib/libbe/be_error.c @@ -107,7 +107,6 @@ int set_error(libbe_handle_t *lbh, be_error_t err) { - /* XXX TODO: should the old error be overwritten or no? */ lbh->error = err; if (lbh->print_on_err && (err != BE_ERR_SUCCESS)) fprintf(stderr, "%s\n", libbe_error_description(lbh)); diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index 0b40a285b6e6..f92d22f4d996 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -64,6 +64,7 @@ typedef struct prop_data { int prop_list_builder_cb(zfs_handle_t *, void *); int be_proplist_update(prop_data_t *); +/* Clobbers any previous errors */ int set_error(libbe_handle_t *, be_error_t); #endif /* _LIBBE_IMPL_H */ From 920abf4df24e6f654af3698d28c58e481bcca6c3 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 03:39:29 +0000 Subject: [PATCH 048/322] libbe(3): Destroy all children of a BE dataset, too This fixes destruction of a deep BE returning an EBUSY because child datasets still exist. --- lib/libbe/be.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 0d6ccf28b6ad..42b450521816 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -181,11 +181,22 @@ be_nicenum(uint64_t num, char *buf, size_t buflen) zfs_nicenum(num, buf, buflen); } +static int +be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) +{ + int err; + + if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0) + return (err); + if ((err = zfs_destroy(zfs_hdl, false)) != 0) + return (err); + return (0); +} + /* * 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 - * TODO: Test destroying a non active but mounted be */ int be_destroy(libbe_handle_t *lbh, char *name, int options) @@ -228,13 +239,14 @@ be_destroy(libbe_handle_t *lbh, char *name, int options) return (set_error(lbh, BE_ERR_DESTROYMNT)); } + if ((err = be_destroy_cb(fs, NULL)) != 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)); + } - /* XXX TODO: convert this to use zfs_iter_children first for deep BEs */ - /* XXX Note: errno 16 (device busy) occurs when chilren are present */ - if ((err = zfs_destroy(fs, false)) != 0) - fprintf(stderr, "delete failed errno: %d\n", errno); - - return (err); + return (0); } From b179da0111bf5734c60d07c110e8abae9cea300f Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 13:46:06 +0000 Subject: [PATCH 049/322] libbe(3)/bectl(8): Standardize copyright headers - File names don't necessarily need to be repeated - Add SPDX tags - Add a missing copyright for Kyle Kneitinger in bectl.8, originally written by him in GSoC 2017; his standard copyright notice has been copied from other files within the same directory to remain consistent with how he clearly wished to portray it --- lib/libbe/be.c | 4 ++-- lib/libbe/be.h | 4 ++-- lib/libbe/be_access.c | 5 +++-- lib/libbe/be_error.c | 4 ++-- lib/libbe/be_impl.h | 4 ++-- lib/libbe/be_info.c | 5 +++-- lib/libbe/libbe.3 | 2 ++ sbin/bectl/bectl.8 | 5 ++++- sbin/bectl/bectl.c | 4 ++-- 9 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 42b450521816..abe1861bac04 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -1,5 +1,5 @@ -/* - * be.c +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * All rights reserved. diff --git a/lib/libbe/be.h b/lib/libbe/be.h index dea934af1dad..b6525a7a1141 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -1,5 +1,5 @@ -/* - * be.h +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * All rights reserved. diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index c0acac215eb7..1d63df186b80 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -1,7 +1,8 @@ -/* - * be_access.c +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger + * Copyright (c) 2018 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c index 58df17ba4e20..becf4ecc13b9 100644 --- a/lib/libbe/be_error.c +++ b/lib/libbe/be_error.c @@ -1,5 +1,5 @@ -/* - * be_error.c +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * All rights reserved. diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index f92d22f4d996..16a3fb9084b4 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -1,5 +1,5 @@ -/* - * be_impl.h +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * All rights reserved. diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index 04bbd71c4f7b..ef4d19ceca3c 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -1,7 +1,8 @@ -/* - * be_info.c +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger + * Copyright (c) 2018 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3 index be026a30e754..c93d74177f07 100644 --- a/lib/libbe/libbe.3 +++ b/lib/libbe/libbe.3 @@ -1,4 +1,6 @@ .\" +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" .\" Copyright (c) 2017 Kyle Kneitinger .\" All rights reserved. .\" diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index 22baa0574c0b..b00b59a50501 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -1,5 +1,8 @@ .\" -.\" be - Utility to manage Boot Environments on the ZFS filesystem +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" Copyright (c) 2017 Kyle J. Kneitinger +.\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index da01840ed196..090ef6b657d2 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -1,5 +1,5 @@ -/* - * be.c +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * All rights reserved. From b6e7c421b75c07ddb0449e1692198dbb3c36547c Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 7 Aug 2018 14:02:41 +0000 Subject: [PATCH 050/322] libbe(3)/bectl(8): Standardize $FreeBSD$ IDs --- lib/libbe/be.c | 3 +++ lib/libbe/be.h | 2 ++ lib/libbe/be_access.c | 3 +++ lib/libbe/be_error.c | 3 +++ lib/libbe/be_impl.h | 2 ++ lib/libbe/be_info.c | 3 +++ sbin/bectl/bectl.8 | 1 + sbin/bectl/bectl.c | 3 +++ sbin/bectl/bectl_jail.c | 5 +++-- sbin/bectl/bectl_list.c | 5 +++-- 10 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index abe1861bac04..e6c11d97e40c 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -26,6 +26,9 @@ * SUCH DAMAGE. */ +#include +__FBSDID("$FreeBSD$"); + #include #include diff --git a/lib/libbe/be.h b/lib/libbe/be.h index b6525a7a1141..4e98533ea630 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -24,6 +24,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef _LIBBE_H diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index 1d63df186b80..e1c4be5fbb3d 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -27,6 +27,9 @@ * SUCH DAMAGE. */ +#include +__FBSDID("$FreeBSD$"); + #include "be.h" #include "be_impl.h" diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c index becf4ecc13b9..0dc77bdbf203 100644 --- a/lib/libbe/be_error.c +++ b/lib/libbe/be_error.c @@ -26,6 +26,9 @@ * SUCH DAMAGE. */ +#include +__FBSDID("$FreeBSD$"); + #include "be.h" #include "be_impl.h" diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h index 16a3fb9084b4..99c783a1a646 100644 --- a/lib/libbe/be_impl.h +++ b/lib/libbe/be_impl.h @@ -24,6 +24,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef _LIBBE_IMPL_H diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c index ef4d19ceca3c..46e5556757c4 100644 --- a/lib/libbe/be_info.c +++ b/lib/libbe/be_info.c @@ -27,6 +27,9 @@ * SUCH DAMAGE. */ +#include +__FBSDID("$FreeBSD$"); + #include "be.h" #include "be_impl.h" diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index b00b59a50501..98fa39860a50 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -15,6 +15,7 @@ .\" .\" .\" @(#)be.1 +.\" .\" $FreeBSD$ .\" .Dd August 5, 2018 diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 090ef6b657d2..ae60f4e2fdc6 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -26,6 +26,9 @@ * SUCH DAMAGE. */ +#include +__FBSDID("$FreeBSD$"); + #include #include #include diff --git a/sbin/bectl/bectl_jail.c b/sbin/bectl/bectl_jail.c index 1731af43514d..d468eb8c1c4c 100644 --- a/sbin/bectl/bectl_jail.c +++ b/sbin/bectl/bectl_jail.c @@ -23,10 +23,11 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include +__FBSDID("$FreeBSD$"); + #include #include #include diff --git a/sbin/bectl/bectl_list.c b/sbin/bectl/bectl_list.c index cbdac3786cc9..0e21d00e19cd 100644 --- a/sbin/bectl/bectl_list.c +++ b/sbin/bectl/bectl_list.c @@ -23,10 +23,11 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include +__FBSDID("$FreeBSD$"); + #include #include #include From 50a1972ed878e14a0bb59c43f452058330865f74 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 8 Aug 2018 03:11:13 +0000 Subject: [PATCH 051/322] Catch up on BE_ERR messages --- lib/libbe/be_error.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c index 0dc77bdbf203..79883451378d 100644 --- a/lib/libbe/be_error.c +++ b/lib/libbe/be_error.c @@ -66,6 +66,12 @@ libbe_error_description(libbe_handle_t *lbh) case BE_ERR_DESTROYMNT: return ("cannot destroy mounted boot env unless forced"); + case BE_ERR_BADPATH: + return ("path not suitable for operation"); + + case BE_ERR_PATHBUSY: + return ("specified path is busy"); + case BE_ERR_PATHLEN: return ("provided path name exceeds maximum length limit"); @@ -87,6 +93,9 @@ libbe_error_description(libbe_handle_t *lbh) case BE_ERR_ZFSCLONE: return ("error when calling zfs_clone() to create boot env"); + case BE_ERR_IO: + return ("input/output error"); + case BE_ERR_UNKNOWN: return ("unknown error"); From 2989df090ae6fa23ea8c75420a37c9054c0d0500 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 8 Aug 2018 03:25:10 +0000 Subject: [PATCH 052/322] libbe(3): Clarify some errors While here, fix a bug with 'rename' that checked the wrong name for being the active BE. --- lib/libbe/be.c | 30 +++++++++++++++--------------- lib/libbe/be.h | 1 + lib/libbe/be_error.c | 3 +++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index e6c11d97e40c..40a838cfc203 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -287,7 +287,14 @@ be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, return (set_error(lbh, BE_ERR_INVALIDNAME)); default: - /* XXX TODO: elaborate return codes */ + /* + * The other errors that zfs_ioc_snapshot might return + * shouldn't happen if we've set things up properly, so + * we'll gloss over them and call it UNKNOWN as it will + * require further triage. + */ + if (errno == ENOTSUP) + return (set_error(lbh, BE_ERR_NOPOOL)); return (set_error(lbh, BE_ERR_UNKNOWN)); } } @@ -598,22 +605,18 @@ be_rename(libbe_handle_t *lbh, char *old, char *new) if ((err = be_root_concat(lbh, new, full_new)) != 0) return (set_error(lbh, err)); - if (be_validate_name(lbh, new) != 0) - return (BE_ERR_UNKNOWN); - /* XXX TODO set and return correct error */ + if ((err = be_validate_name(lbh, new)) != 0) + return (err); /* Check if old is active BE */ - if (strcmp(full_new, be_active_path(lbh)) == 0) - return (BE_ERR_UNKNOWN); - /* XXX TODO set and return correct error */ + 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 (BE_ERR_UNKNOWN); - /* XXX TODO set and return correct error */ + return (set_error(lbh, BE_ERR_NOENT)); if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) - return (BE_ERR_UNKNOWN); - /* XXX TODO set and return correct error */ + return (set_error(lbh, BE_ERR_EXISTS)); /* XXX TODO * - What about mounted BEs? @@ -621,14 +624,11 @@ be_rename(libbe_handle_t *lbh, char *old, char *new) */ if ((zfs_hdl = zfs_open(lbh->lzh, full_old, ZFS_TYPE_FILESYSTEM)) == NULL) - return (BE_ERR_UNKNOWN); - /* XXX TODO set and return correct error */ - + return (set_error(lbh, BE_ERR_ZFSOPEN)); /* recurse, nounmount, forceunmount */ struct renameflags flags = { 0, 0, 0 }; - /* XXX TODO: error log on this call */ err = zfs_rename(zfs_hdl, NULL, full_new, flags); zfs_close(zfs_hdl); diff --git a/lib/libbe/be.h b/lib/libbe/be.h index 4e98533ea630..daf04cf70edf 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -56,6 +56,7 @@ typedef enum be_error { BE_ERR_ZFSOPEN, /* calling zfs_open() failed */ BE_ERR_ZFSCLONE, /* error when calling zfs_clone to create be */ BE_ERR_IO, /* error when doing some I/O operation */ + BE_ERR_NOPOOL, /* operation not supported on this pool */ BE_ERR_UNKNOWN, /* unknown error */ } be_error_t; diff --git a/lib/libbe/be_error.c b/lib/libbe/be_error.c index 79883451378d..355d742f8b74 100644 --- a/lib/libbe/be_error.c +++ b/lib/libbe/be_error.c @@ -96,6 +96,9 @@ libbe_error_description(libbe_handle_t *lbh) case BE_ERR_IO: return ("input/output error"); + case BE_ERR_NOPOOL: + return ("operation not supported on this pool"); + case BE_ERR_UNKNOWN: return ("unknown error"); From 506f5fdf2b304197067e094354197e49c35b8bf1 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 8 Aug 2018 03:46:12 +0000 Subject: [PATCH 053/322] libbe(3): Some more light error handling... --- lib/libbe/be.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index 40a838cfc203..e5dfc53ed0de 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -652,7 +652,7 @@ be_export(libbe_handle_t *lbh, char *bootenv, int fd) be_root_concat(lbh, snap_name, buf); if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) - return (BE_ERR_ZFSOPEN); + return (set_error(lbh, BE_ERR_ZFSOPEN)); err = zfs_send_one(zfs, NULL, fd, 0); return (err); @@ -667,14 +667,22 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) nvlist_t *props; zfs_handle_t *zfs; int err, len; + char nbuf[24]; /* - * XXX TODO: this is a very likely name for someone to already have - * used... we should avoid it. + * 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. */ - if ((err = be_root_concat(lbh, "libbe_import_temp", buf)) != 0) - /* XXX TODO error handle */ - return (-1); + 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)); time(&rawtime); len = strlen(buf); @@ -683,18 +691,20 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { - /* TODO: go through libzfs_core's recv_impl and find returned - * errors and set appropriate BE_ERR - * edit: errors are not in libzfs_core, my assumption is - * that they use libzfs errors - * note: 17 is err for dataset already existing - */ - return (err); + switch (err) { + case EINVAL: + return (set_error(lbh, BE_ERR_NOORIGIN)); + case ENOENT: + return (set_error(lbh, BE_ERR_NOENT)); + case EIO: + return (set_error(lbh, BE_ERR_IO)); + default: + return (set_error(lbh, BE_ERR_UNKNOWN)); + } } if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) - /* XXX TODO correct error */ - return (-1); + return (set_error(lbh, BE_ERR_ZFSOPEN)); nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); @@ -707,7 +717,7 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) nvlist_free(props); - /* XXX TODO: recursively delete be_import_temp dataset */ + /* XXX TODO: recursively delete nbuf dataset */ return (err); } From 73c3d60843efc6116e8dc3bbbf8c32948490d850 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 10 Aug 2018 04:01:40 +0000 Subject: [PATCH 054/322] libbe(3): more small cleanup, const'ify and light style(9) --- lib/libbe/be.c | 34 ++++++++++++++++------------------ lib/libbe/be.h | 14 +++++++------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index e5dfc53ed0de..fb32d087c0da 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -202,7 +202,7 @@ be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) * BE_DESTROY_FORCE : forces operation on mounted datasets */ int -be_destroy(libbe_handle_t *lbh, char *name, int options) +be_destroy(libbe_handle_t *lbh, const char *name, int options) { zfs_handle_t *fs; char path[BE_MAXPATHLEN]; @@ -307,7 +307,7 @@ be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, * Create the boot environment specified by the name parameter */ int -be_create(libbe_handle_t *lbh, char *name) +be_create(libbe_handle_t *lbh, const char *name) { int err; @@ -328,11 +328,8 @@ be_deep_clone_prop(int prop, void *cb) dccb = cb; /* Skip some properties we don't want to touch */ - switch (prop) { - case ZFS_PROP_CANMOUNT: - return (ZPROP_CONT); - break; - } + if (prop == ZFS_PROP_CANMOUNT) + return (ZPROP_CONT); /* Don't copy readonly properties */ if (zfs_prop_readonly(prop)) @@ -593,7 +590,7 @@ be_validate_name(libbe_handle_t *lbh __unused, const char *name) * usage */ int -be_rename(libbe_handle_t *lbh, char *old, char *new) +be_rename(libbe_handle_t *lbh, const char *old, const char *new) { char full_old[BE_MAXPATHLEN]; char full_new[BE_MAXPATHLEN]; @@ -638,7 +635,7 @@ be_rename(libbe_handle_t *lbh, char *old, char *new) int -be_export(libbe_handle_t *lbh, char *bootenv, int fd) +be_export(libbe_handle_t *lbh, const char *bootenv, int fd) { char snap_name[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; @@ -660,7 +657,7 @@ be_export(libbe_handle_t *lbh, char *bootenv, int fd) int -be_import(libbe_handle_t *lbh, char *bootenv, int fd) +be_import(libbe_handle_t *lbh, const char *bootenv, int fd) { char buf[BE_MAXPATHLEN]; time_t rawtime; @@ -723,14 +720,16 @@ be_import(libbe_handle_t *lbh, char *bootenv, int fd) int -be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) +be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) { + struct stat sb; char active[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; nvlist_t *props; + const char *s; zfs_handle_t *zfs; - struct stat sb; - int err; + long snap_name; + int err, pos; /* Require absolute paths */ if (*child_path != '/') @@ -741,7 +740,7 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) strcpy(buf, active); /* Create non-mountable parent dataset(s) */ - char *s = child_path; + s = child_path; for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { size_t len = p - s; strncat(buf, s, len); @@ -753,9 +752,8 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) nvlist_free(props); } - /* Path does not exist as a descendent of / yet */ - int pos = strlen(active); + pos = strlen(active); /* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */ strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); @@ -797,7 +795,7 @@ be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) */ /* XXX TODO: use mktemp */ - long int snap_name = random(); + snap_name = random(); snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); @@ -854,7 +852,7 @@ be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, int -be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) +be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) { char be_path[BE_MAXPATHLEN]; char buf[BE_MAXPATHLEN]; diff --git a/lib/libbe/be.h b/lib/libbe/be.h index daf04cf70edf..157ed41b0b1a 100644 --- a/lib/libbe/be.h +++ b/lib/libbe/be.h @@ -78,16 +78,16 @@ int be_get_dataset_snapshots(libbe_handle_t *, const char *, nvlist_t *); int be_prop_list_alloc(nvlist_t **be_list); void be_prop_list_free(nvlist_t *be_list); -int be_activate(libbe_handle_t *, char *, bool); +int be_activate(libbe_handle_t *, const char *, bool); /* Bootenv creation functions */ -int be_create(libbe_handle_t *, char *); +int be_create(libbe_handle_t *, const char *); 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 *); /* Bootenv manipulation functions */ -int be_rename(libbe_handle_t *, char *, char *); +int be_rename(libbe_handle_t *, const char *, const char *); /* Bootenv removal functions */ @@ -95,7 +95,7 @@ typedef enum { BE_DESTROY_FORCE = 1 << 0, } be_destroy_opt_t; -int be_destroy(libbe_handle_t *, char *, int); +int be_destroy(libbe_handle_t *, const char *, int); /* Bootenv mounting functions: be_access.c */ @@ -119,10 +119,10 @@ 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_export(libbe_handle_t *, char *, int fd); -int be_import(libbe_handle_t *, char *, int fd); +int be_export(libbe_handle_t *, const char *, int fd); +int be_import(libbe_handle_t *, const char *, int fd); -int be_add_child(libbe_handle_t *, char *, bool); +int be_add_child(libbe_handle_t *, const char *, bool); void be_nicenum(uint64_t num, char *buf, size_t buflen); #endif /* _LIBBE_H */ From 6d4b1d241d7b3e471c61cae3775d511e6864e3b3 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 10 Aug 2018 04:23:13 +0000 Subject: [PATCH 055/322] libbe(3): Plug some holes, do some more proper error returns For those returning just -1 before, have them set ERR_UNKNOWN for now. --- lib/libbe/be.c | 63 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/lib/libbe/be.c b/lib/libbe/be.c index fb32d087c0da..4ea1d677dfc2 100644 --- a/lib/libbe/be.c +++ b/lib/libbe/be.c @@ -615,14 +615,16 @@ be_rename(libbe_handle_t *lbh, const char *old, const char *new) if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) return (set_error(lbh, BE_ERR_EXISTS)); - /* XXX TODO - * - What about mounted BEs? - * - if mounted error out unless a force flag is set? - */ if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 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 }; @@ -643,8 +645,8 @@ be_export(libbe_handle_t *lbh, const char *bootenv, int fd) int err; if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) - /* XXX TODO error handle */ - return (-1); + /* Use the error set by be_snapshot */ + return (err); be_root_concat(lbh, snap_name, buf); @@ -652,6 +654,8 @@ be_export(libbe_handle_t *lbh, const char *bootenv, int fd) return (set_error(lbh, BE_ERR_ZFSOPEN)); err = zfs_send_one(zfs, NULL, fd, 0); + zfs_close(zfs); + return (err); } @@ -714,8 +718,7 @@ be_import(libbe_handle_t *lbh, const char *bootenv, int fd) nvlist_free(props); - /* XXX TODO: recursively delete nbuf dataset */ - return (err); + return (be_destroy(lbh, nbuf, 0)); } @@ -728,15 +731,13 @@ be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) nvlist_t *props; const char *s; zfs_handle_t *zfs; - long snap_name; - int err, pos; + int err; /* Require absolute paths */ if (*child_path != '/') - /* XXX TODO: create appropriate error */ - return (-1); + return (set_error(lbh, BE_ERR_BADPATH)); - strncpy(active, be_active_path(lbh), BE_MAXPATHLEN); + strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); strcpy(buf, active); /* Create non-mountable parent dataset(s) */ @@ -753,16 +754,13 @@ be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) } /* Path does not exist as a descendent of / yet */ - pos = strlen(active); - - /* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */ - strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); + if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) + return (set_error(lbh, BE_ERR_PATHLEN)); if (stat(child_path, &sb) != 0) { /* Verify that error is ENOENT */ - if (errno != 2) - /* XXX TODO: create appropriate error */ - return (-1); + if (errno != ENOENT) + return (set_error(lbh, BE_ERR_NOENT)); nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); nvlist_add_string(props, "canmount", "noauto"); @@ -772,18 +770,17 @@ be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) /* XXX TODO handle error */ - return (-1); + return (set_error(lbh, BE_ERR_UNKNOWN)); nvlist_free(props); if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) - /* XXX TODO handle error */ - return (-1); + return (set_error(lbh, BE_ERR_ZFSOPEN)); /* Set props */ if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) /* TODO handle error */ - return (-1); + return (set_error(lbh, BE_ERR_UNKNOWN)); } else if (cp_if_exists) { /* Path is already a descendent of / and should be copied */ @@ -793,30 +790,28 @@ be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) * Establish if the existing path is a zfs dataset or just * the subdirectory of one */ - - /* XXX TODO: use mktemp */ - snap_name = random(); - - snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); + strlcpy(buf, "/tmp/be_snap.XXXXX", sizeof(buf)); + if (mktemp(buf) == NULL) + return (set_error(lbh, BE_ERR_UNKNOWN)); if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) /* XXX TODO correct error */ - return (-1); + return (set_error(lbh, BE_ERR_UNKNOWN)); /* Clone */ if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) - /* XXX TODO correct error */ - return (-1); + return (BE_ERR_ZFSOPEN); if ((err = zfs_clone(zfs, active, NULL)) != 0) /* XXX TODO correct error */ - return (-1); + return (set_error(lbh, BE_ERR_UNKNOWN)); /* set props */ + zfs_close(zfs); } else /* TODO: error code for exists, but not cp? */ - return (-1); + return (set_error(lbh, BE_ERR_EXISTS)); return (BE_ERR_SUCCESS); } From c6014c64fcfd099d0087da3509e8a623d2c30209 Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Fri, 10 Aug 2018 16:34:24 +0000 Subject: [PATCH 056/322] Import ACPICA 20180810. --- changes.txt | 47 ++++++ source/components/debugger/dbinput.c | 10 +- source/components/debugger/dbmethod.c | 7 +- source/components/debugger/dbxface.c | 10 +- source/components/dispatcher/dsfield.c | 26 ++++ source/components/hardware/hwregs.c | 11 +- source/components/hardware/hwsleep.c | 19 ++- source/components/namespace/nsaccess.c | 16 +++ source/components/parser/psloop.c | 41 +++++- source/components/tables/tbdata.c | 4 +- source/components/utilities/utdelete.c | 7 +- source/components/utilities/uterror.c | 6 +- source/components/utilities/utstrsuppt.c | 30 +++- source/components/utilities/utstrtoul64.c | 2 +- source/include/acconfig.h | 2 +- source/include/acexcep.h | 7 + source/include/aclocal.h | 1 + source/include/acnamesp.h | 17 +-- source/include/acpixf.h | 2 +- source/include/acutils.h | 4 + source/tools/acpiexec/aecommon.h | 18 ++- source/tools/acpiexec/aeinitfile.c | 167 +++++++++++++--------- source/tools/acpiexec/aemain.c | 11 +- source/tools/acpiexec/aeregion.c | 8 +- 24 files changed, 359 insertions(+), 114 deletions(-) diff --git a/changes.txt b/changes.txt index b529ab61277a..d2a76e90a82c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,50 @@ +---------------------------------------- +10 August 2018. Summary of changes for version 20180810: + + +1) ACPICA kernel-resident subsystem: + +Initial ACPI table loading: Attempt to continue loading ACPI tables +regardless of malformed AML. Since migrating table initialization to the +new module-level code support, the AML interpreter rejected tables upon +any ACPI error encountered during table load. This is a problem because +non-serious ACPI errors during table load do not necessarily mean that +the entire definition block (DSDT or SSDT) is invalid. This change +improves the table loading by ignoring some types of errors that can be +generated by incorrect AML. This can range from object type errors, scope +errors, and index errors. + +Suspend/Resume support: Update to stop unconditionally clearing ACPI IRQs +during suspend/resume. The status of ACPI events is no longer cleared +when entering the ACPI S5 system state (power off) which caused some +systems to power up immediately after turning off power in certain +situations. This was a functional regression. It was fixed by clearing +the status of all ACPI events again when entering S5 (for system-wide +suspend or hibernation the clearing of the status of all events is not +desirable, as it might cause the kernel to miss wakeup events sometimes). +Rafael Wysocki. + + +2) iASL Compiler/Disassembler and Tools: + +AcpiExec: Enhanced the -fi option (Namespace initialization file). Field +elements listed in the initialization file were previously initialized +after the table load and before executing module-level code blocks. +Recent changes in the module-level code support means that the table load +becomes a large control method execution. If fields are used within +module-level code and we are executing with the -fi option, the +initialization values were used to initialize the namespace object(s) +only after the table was finished loading. This change Provides an early +initialization of objects specified in the initialization file so that +field unit values are populated during the table load (not after the +load). + +AcpiExec: Fixed a small memory leak regression that could result in +warnings during exit of the utility. These warnings were similar to +these: + 0002D690 Length 0x0006 nsnames-0502 [Not a Descriptor - too small] + 0002CD70 Length 0x002C utcache-0453 [Operand] Integer RefCount 0x0001 + ---------------------------------------- 29 June 2018. Summary of changes for version 20180629: diff --git a/source/components/debugger/dbinput.c b/source/components/debugger/dbinput.c index 75e7a37f5385..0daca95b7e65 100644 --- a/source/components/debugger/dbinput.c +++ b/source/components/debugger/dbinput.c @@ -969,7 +969,11 @@ AcpiDbCommandDispatch ( case CMD_DISASSEMBLE: case CMD_DISASM: +#ifdef ACPI_DISASSEMBLER (void) AcpiDbDisassembleMethod (AcpiGbl_DbArgs[1]); +#else + AcpiOsPrintf ("The AML Disassembler is not configured/present\n"); +#endif break; case CMD_DUMP: @@ -1083,7 +1087,11 @@ AcpiDbCommandDispatch ( case CMD_LIST: - AcpiDbDisassembleAml (AcpiGbl_DbArgs[1], Op); +#ifdef ACPI_DISASSEMBLER + AcpiDbDisassembleAml (AcpiGbl_DbArgs[1], Op);; +#else + AcpiOsPrintf ("The AML Disassembler is not configured/present\n"); +#endif break; case CMD_LOCKS: diff --git a/source/components/debugger/dbmethod.c b/source/components/debugger/dbmethod.c index d6c6aa55bf6a..a030823c2ac7 100644 --- a/source/components/debugger/dbmethod.c +++ b/source/components/debugger/dbmethod.c @@ -393,6 +393,7 @@ AcpiDbSetMethodData ( } +#ifdef ACPI_DISASSEMBLER /******************************************************************************* * * FUNCTION: AcpiDbDisassembleAml @@ -426,9 +427,7 @@ AcpiDbDisassembleAml ( NumStatements = strtoul (Statements, NULL, 0); } -#ifdef ACPI_DISASSEMBLER AcpiDmDisassemble (NULL, Op, NumStatements); -#endif } @@ -511,8 +510,6 @@ AcpiDbDisassembleMethod ( WalkState->ParseFlags |= ACPI_PARSE_DISASSEMBLE; Status = AcpiPsParseAml (WalkState); - -#ifdef ACPI_DISASSEMBLER (void) AcpiDmParseDeferredOps (Op); /* Now we can disassemble the method */ @@ -520,7 +517,6 @@ AcpiDbDisassembleMethod ( AcpiGbl_DmOpt_Verbose = FALSE; AcpiDmDisassemble (NULL, Op, 0); AcpiGbl_DmOpt_Verbose = TRUE; -#endif AcpiPsDeleteParseTree (Op); @@ -531,6 +527,7 @@ AcpiDbDisassembleMethod ( AcpiUtReleaseOwnerId (&ObjDesc->Method.OwnerId); return (AE_OK); } +#endif /******************************************************************************* diff --git a/source/components/debugger/dbxface.c b/source/components/debugger/dbxface.c index 0b964f69ff5f..c661b248b75e 100644 --- a/source/components/debugger/dbxface.c +++ b/source/components/debugger/dbxface.c @@ -154,6 +154,7 @@ #include "amlcode.h" #include "acdebug.h" #include "acinterp.h" +#include "acparser.h" #define _COMPONENT ACPI_CA_DEBUGGER @@ -435,10 +436,17 @@ AcpiDbSingleStep ( } } - /* Now we can display it */ + /* Now we can disassemble and display it */ #ifdef ACPI_DISASSEMBLER AcpiDmDisassemble (WalkState, DisplayOp, ACPI_UINT32_MAX); +#else + /* + * The AML Disassembler is not configured - at least we can + * display the opcode value and name + */ + AcpiOsPrintf ("AML Opcode: %4.4X %s\n", Op->Common.AmlOpcode, + AcpiPsGetOpcodeName (Op->Common.AmlOpcode)); #endif if ((Op->Common.AmlOpcode == AML_IF_OP) || diff --git a/source/components/dispatcher/dsfield.c b/source/components/dispatcher/dsfield.c index df770c1c07b3..3db39fb8b25d 100644 --- a/source/components/dispatcher/dsfield.c +++ b/source/components/dispatcher/dsfield.c @@ -157,6 +157,10 @@ #include "acnamesp.h" #include "acparser.h" +#ifdef ACPI_EXEC_APP +#include "aecommon.h" +#endif + #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME ("dsfield") @@ -430,6 +434,13 @@ AcpiDsGetFieldNames ( UINT64 Position; ACPI_PARSE_OBJECT *Child; +#ifdef ACPI_EXEC_APP + UINT64 Value = 0; + ACPI_OPERAND_OBJECT *ResultDesc; + ACPI_OPERAND_OBJECT *ObjDesc; + char *NamePath; +#endif + ACPI_FUNCTION_TRACE_PTR (DsGetFieldNames, Info); @@ -564,6 +575,18 @@ AcpiDsGetFieldNames ( { return_ACPI_STATUS (Status); } +#ifdef ACPI_EXEC_APP + NamePath = AcpiNsGetExternalPathname (Info->FieldNode); + ObjDesc = AcpiUtCreateIntegerObject (Value); + if (ACPI_SUCCESS (AeLookupInitFileEntry (NamePath, &Value))) + { + AcpiExWriteDataToField (ObjDesc, + AcpiNsGetAttachedObject (Info->FieldNode), + &ResultDesc); + } + AcpiUtRemoveReference (ObjDesc); + ACPI_FREE (NamePath); +#endif } } @@ -756,6 +779,9 @@ AcpiDsInitFieldObjects ( Flags |= ACPI_NS_TEMPORARY; } +#ifdef ACPI_EXEC_APP + Flags |= ACPI_NS_OVERRIDE_IF_FOUND; +#endif /* * Walk the list of entries in the FieldList * Note: FieldList can be of zero length. In this case, Arg will be NULL. diff --git a/source/components/hardware/hwregs.c b/source/components/hardware/hwregs.c index c0deed339797..ec106847ba3a 100644 --- a/source/components/hardware/hwregs.c +++ b/source/components/hardware/hwregs.c @@ -724,13 +724,20 @@ AcpiHwRegisterRead ( case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ Status = AcpiHwRead (&Value64, &AcpiGbl_FADT.XPm2ControlBlock); - Value = (UINT32) Value64; + if (ACPI_SUCCESS (Status)) + { + Value = (UINT32) Value64; + } break; case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ Status = AcpiHwRead (&Value64, &AcpiGbl_FADT.XPmTimerBlock); - Value = (UINT32) Value64; + if (ACPI_SUCCESS (Status)) + { + Value = (UINT32) Value64; + } + break; case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ diff --git a/source/components/hardware/hwsleep.c b/source/components/hardware/hwsleep.c index fe111872cec0..7e435acecba0 100644 --- a/source/components/hardware/hwsleep.c +++ b/source/components/hardware/hwsleep.c @@ -198,17 +198,28 @@ AcpiHwLegacySleep ( return_ACPI_STATUS (Status); } - /* - * 1) Disable all GPEs - * 2) Enable all wakeup GPEs - */ + /* Disable all GPEs */ + Status = AcpiHwDisableAllGpes (); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } + /* + * If the target sleep state is S5, clear all GPEs and fixed events too + */ + if (SleepState == ACPI_STATE_S5) + { + Status = AcpiHwClearAcpiStatus(); + if (ACPI_FAILURE (Status)) + { + return_ACPI_STATUS (Status); + } + } AcpiGbl_SystemAwakeAndRunning = FALSE; + /* Enable all wakeup GPEs */ + Status = AcpiHwEnableAllWakeupGpes (); if (ACPI_FAILURE (Status)) { diff --git a/source/components/namespace/nsaccess.c b/source/components/namespace/nsaccess.c index bf530d483025..af97c51feff5 100644 --- a/source/components/namespace/nsaccess.c +++ b/source/components/namespace/nsaccess.c @@ -724,6 +724,15 @@ AcpiNsLookup ( CurrentNode)); } +#ifdef ACPI_EXEC_APP + if ((Status == AE_ALREADY_EXISTS) && + (ThisNode->Flags & ANOBJ_NODE_EARLY_INIT)) + { + ThisNode->Flags &= ~ANOBJ_NODE_EARLY_INIT; + Status = AE_OK; + } +#endif + #ifdef ACPI_ASL_COMPILER /* * If this ACPI name already exists within the namespace as an @@ -845,6 +854,13 @@ AcpiNsLookup ( } } +#ifdef ACPI_EXEC_APP + if (Flags & ACPI_NS_EARLY_INIT) + { + ThisNode->Flags |= ANOBJ_NODE_EARLY_INIT; + } +#endif + *ReturnNode = ThisNode; return_ACPI_STATUS (AE_OK); } diff --git a/source/components/parser/psloop.c b/source/components/parser/psloop.c index fd6773c149c3..ef517b54d0bf 100644 --- a/source/components/parser/psloop.c +++ b/source/components/parser/psloop.c @@ -164,6 +164,7 @@ #include "acdispat.h" #include "amlcode.h" #include "acconvert.h" +#include "acnamesp.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME ("psloop") @@ -645,6 +646,18 @@ AcpiPsParseLoop ( Status = AcpiPsCreateOp (WalkState, AmlOpStart, &Op); if (ACPI_FAILURE (Status)) { + /* + * ACPI_PARSE_MODULE_LEVEL means that we are loading a table by + * executing it as a control method. However, if we encounter + * an error while loading the table, we need to keep trying to + * load the table rather than aborting the table load. Set the + * status to AE_OK to proceed with the table load. + */ + if ((WalkState->ParseFlags & ACPI_PARSE_MODULE_LEVEL) && + Status == AE_ALREADY_EXISTS) + { + Status = AE_OK; + } if (Status == AE_CTRL_PARSE_CONTINUE) { continue; @@ -665,18 +678,20 @@ AcpiPsParseLoop ( { return_ACPI_STATUS (Status); } - if (WalkState->Opcode == AML_SCOPE_OP) + if (AcpiNsOpensScope ( + AcpiPsGetOpcodeInfo (WalkState->Opcode)->ObjectType)) { /* - * If the scope op fails to parse, skip the body of the - * scope op because the parse failure indicates that the - * device may not exist. + * If the scope/device op fails to parse, skip the body of + * the scope op because the parse failure indicates that + * the device may not exist. */ + ACPI_ERROR ((AE_INFO, "Skip parsing opcode %s", + AcpiPsGetOpcodeName (WalkState->Opcode))); WalkState->ParserState.Aml = WalkState->Aml + 1; WalkState->ParserState.Aml = AcpiPsGetNextPackageEnd(&WalkState->ParserState); WalkState->Aml = WalkState->ParserState.Aml; - ACPI_ERROR ((AE_INFO, "Skipping Scope block")); } continue; @@ -834,6 +849,22 @@ AcpiPsParseLoop ( { Status = AE_OK; } + else if ((WalkState->ParseFlags & ACPI_PARSE_MODULE_LEVEL) && + (ACPI_AML_EXCEPTION(Status) || Status == AE_ALREADY_EXISTS || + Status == AE_NOT_FOUND)) + { + /* + * ACPI_PARSE_MODULE_LEVEL flag means that we are currently + * loading a table by executing it as a control method. + * However, if we encounter an error while loading the table, + * we need to keep trying to load the table rather than + * aborting the table load (setting the status to AE_OK + * continues the table load). If we get a failure at this + * point, it means that the dispatcher got an error while + * trying to execute the Op. + */ + Status = AE_OK; + } } Status = AcpiPsCompleteOp (WalkState, &Op, Status); diff --git a/source/components/tables/tbdata.c b/source/components/tables/tbdata.c index 61a2d340d56d..7b44fc74eb23 100644 --- a/source/components/tables/tbdata.c +++ b/source/components/tables/tbdata.c @@ -711,9 +711,9 @@ AcpiTbVerifyTempTable ( { if (Status != AE_CTRL_TERMINATE) { - ACPI_EXCEPTION ((AE_INFO, AE_NO_MEMORY, + ACPI_EXCEPTION ((AE_INFO, Status, "%4.4s 0x%8.8X%8.8X" - " Table is duplicated", + " Table is already loaded", AcpiUtValidNameseg (TableDesc->Signature.Ascii) ? TableDesc->Signature.Ascii : "????", ACPI_FORMAT_UINT64 (TableDesc->Address))); diff --git a/source/components/utilities/utdelete.c b/source/components/utilities/utdelete.c index 4565d8fa5ae3..da2e31ff9fd5 100644 --- a/source/components/utilities/utdelete.c +++ b/source/components/utilities/utdelete.c @@ -520,6 +520,7 @@ AcpiUtUpdateRefCount ( UINT16 OriginalCount; UINT16 NewCount = 0; ACPI_CPU_FLAGS LockFlags; + char *Message; ACPI_FUNCTION_NAME (UtUpdateRefCount); @@ -560,6 +561,7 @@ AcpiUtUpdateRefCount ( "Obj %p Type %.2X [%s] Refs %.2X [Incremented]\n", Object, Object->Common.Type, AcpiUtGetObjectTypeName (Object), NewCount)); + Message = "Incremement"; break; case REF_DECREMENT: @@ -591,6 +593,7 @@ AcpiUtUpdateRefCount ( { AcpiUtDeleteInternalObj (Object); } + Message = "Decrement"; break; default: @@ -608,8 +611,8 @@ AcpiUtUpdateRefCount ( if (NewCount > ACPI_MAX_REFERENCE_COUNT) { ACPI_WARNING ((AE_INFO, - "Large Reference Count (0x%X) in object %p, Type=0x%.2X", - NewCount, Object, Object->Common.Type)); + "Large Reference Count (0x%X) in object %p, Type=0x%.2X Operation=%s", + NewCount, Object, Object->Common.Type, Message)); } } diff --git a/source/components/utilities/uterror.c b/source/components/utilities/uterror.c index eca92f3b1b5c..67c22330f069 100644 --- a/source/components/utilities/uterror.c +++ b/source/components/utilities/uterror.c @@ -352,19 +352,19 @@ AcpiUtPrefixedNamespaceError ( { case AE_ALREADY_EXISTS: - AcpiOsPrintf ("\n" ACPI_MSG_BIOS_ERROR); + AcpiOsPrintf (ACPI_MSG_BIOS_ERROR); Message = "Failure creating"; break; case AE_NOT_FOUND: - AcpiOsPrintf ("\n" ACPI_MSG_BIOS_ERROR); + AcpiOsPrintf (ACPI_MSG_BIOS_ERROR); Message = "Could not resolve"; break; default: - AcpiOsPrintf ("\n" ACPI_MSG_ERROR); + AcpiOsPrintf (ACPI_MSG_ERROR); Message = "Failure resolving"; break; } diff --git a/source/components/utilities/utstrsuppt.c b/source/components/utilities/utstrsuppt.c index 2e7232a3b05f..b482da21db5e 100644 --- a/source/components/utilities/utstrsuppt.c +++ b/source/components/utilities/utstrsuppt.c @@ -419,15 +419,39 @@ BOOLEAN AcpiUtDetectHexPrefix ( char **String) { + char *InitialPosition = *String; + AcpiUtRemoveHexPrefix (String); + if (*String != InitialPosition) + { + return (TRUE); /* String is past leading 0x */ + } + + return (FALSE); /* Not a hex string */ +} + + +/******************************************************************************* + * + * FUNCTION: AcpiUtRemoveHexPrefix + * + * PARAMETERS: String - Pointer to input ASCII string + * + * RETURN: none + * + * DESCRIPTION: Remove a hex "0x" prefix + * + ******************************************************************************/ + +void +AcpiUtRemoveHexPrefix ( + char **String) +{ if ((**String == ACPI_ASCII_ZERO) && (tolower ((int) *(*String + 1)) == 'x')) { *String += 2; /* Go past the leading 0x */ - return (TRUE); } - - return (FALSE); /* Not a hex string */ } diff --git a/source/components/utilities/utstrtoul64.c b/source/components/utilities/utstrtoul64.c index 5bbf24fd8014..77190e107991 100644 --- a/source/components/utilities/utstrtoul64.c +++ b/source/components/utilities/utstrtoul64.c @@ -383,7 +383,7 @@ AcpiUtImplicitStrtoul64 ( * implicit conversions, and the "0x" prefix is "not allowed". * However, allow a "0x" prefix as an ACPI extension. */ - AcpiUtDetectHexPrefix (&String); + AcpiUtRemoveHexPrefix (&String); if (!AcpiUtRemoveLeadingZeros (&String)) { diff --git a/source/include/acconfig.h b/source/include/acconfig.h index c5a67ca04755..41f3dff0822b 100644 --- a/source/include/acconfig.h +++ b/source/include/acconfig.h @@ -233,7 +233,7 @@ /* Maximum object reference count (detects object deletion issues) */ -#define ACPI_MAX_REFERENCE_COUNT 0x800 +#define ACPI_MAX_REFERENCE_COUNT 0x4000 /* Default page size for use in mapping memory for operation regions */ diff --git a/source/include/acexcep.h b/source/include/acexcep.h index 828f9fb9485c..ec2304c550dd 100644 --- a/source/include/acexcep.h +++ b/source/include/acexcep.h @@ -204,6 +204,13 @@ typedef struct acpi_exception_info #define AE_OK (ACPI_STATUS) 0x0000 +#define ACPI_ENV_EXCEPTION(Status) (Status & AE_CODE_ENVIRONMENTAL) +#define ACPI_AML_EXCEPTION(Status) (Status & AE_CODE_AML) +#define ACPI_PROG_EXCEPTION(Status) (Status & AE_CODE_PROGRAMMER) +#define ACPI_TABLE_EXCEPTION(Status) (Status & AE_CODE_ACPI_TABLES) +#define ACPI_CNTL_EXCEPTION(Status) (Status & AE_CODE_CONTROL) + + /* * Environmental exceptions */ diff --git a/source/include/aclocal.h b/source/include/aclocal.h index 58c09379ccdc..2e2bcd0d23f5 100644 --- a/source/include/aclocal.h +++ b/source/include/aclocal.h @@ -327,6 +327,7 @@ typedef struct acpi_namespace_node #define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */ #define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */ #define ANOBJ_ALLOCATED_BUFFER 0x40 /* Method AML buffer is dynamic (InstallMethod) */ +#define ANOBJ_NODE_EARLY_INIT 0x80 /* AcpiExec only: Node was create via init file (-fi) */ #define ANOBJ_IS_EXTERNAL 0x08 /* iASL only: This object created via External() */ #define ANOBJ_METHOD_NO_RETVAL 0x10 /* iASL only: Method has no return value */ diff --git a/source/include/acnamesp.h b/source/include/acnamesp.h index 529fc6e78a36..1e3db8d1cadd 100644 --- a/source/include/acnamesp.h +++ b/source/include/acnamesp.h @@ -168,14 +168,15 @@ /* Flags for AcpiNsLookup, AcpiNsSearchAndEnter */ #define ACPI_NS_NO_UPSEARCH 0 -#define ACPI_NS_SEARCH_PARENT 0x01 -#define ACPI_NS_DONT_OPEN_SCOPE 0x02 -#define ACPI_NS_NO_PEER_SEARCH 0x04 -#define ACPI_NS_ERROR_IF_FOUND 0x08 -#define ACPI_NS_PREFIX_IS_SCOPE 0x10 -#define ACPI_NS_EXTERNAL 0x20 -#define ACPI_NS_TEMPORARY 0x40 -#define ACPI_NS_OVERRIDE_IF_FOUND 0x80 +#define ACPI_NS_SEARCH_PARENT 0x0001 +#define ACPI_NS_DONT_OPEN_SCOPE 0x0002 +#define ACPI_NS_NO_PEER_SEARCH 0x0004 +#define ACPI_NS_ERROR_IF_FOUND 0x0008 +#define ACPI_NS_PREFIX_IS_SCOPE 0x0010 +#define ACPI_NS_EXTERNAL 0x0020 +#define ACPI_NS_TEMPORARY 0x0040 +#define ACPI_NS_OVERRIDE_IF_FOUND 0x0080 +#define ACPI_NS_EARLY_INIT 0x0100 /* Flags for AcpiNsWalkNamespace */ diff --git a/source/include/acpixf.h b/source/include/acpixf.h index 110b375b464d..5097fcc1b732 100644 --- a/source/include/acpixf.h +++ b/source/include/acpixf.h @@ -154,7 +154,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20180629 +#define ACPI_CA_VERSION 0x20180810 #include "acconfig.h" #include "actypes.h" diff --git a/source/include/acutils.h b/source/include/acutils.h index 8dfce9016940..1f7608843b00 100644 --- a/source/include/acutils.h +++ b/source/include/acutils.h @@ -362,6 +362,10 @@ BOOLEAN AcpiUtDetectHexPrefix ( char **String); +void +AcpiUtRemoveHexPrefix ( + char **String); + BOOLEAN AcpiUtDetectOctalPrefix ( char **String); diff --git a/source/tools/acpiexec/aecommon.h b/source/tools/acpiexec/aecommon.h index 233edc2b4877..8ada9021f67e 100644 --- a/source/tools/acpiexec/aecommon.h +++ b/source/tools/acpiexec/aecommon.h @@ -189,11 +189,22 @@ typedef struct ae_debug_regions } AE_DEBUG_REGIONS; +/* + * Init file entry + */ +typedef struct init_file_entry +{ + char *Name; + UINT64 Value; +} INIT_FILE_ENTRY; + extern BOOLEAN AcpiGbl_UseLocalFaultHandler; extern BOOLEAN AcpiGbl_VerboseHandlers; extern BOOLEAN AcpiGbl_IgnoreErrors; extern BOOLEAN AcpiGbl_AbortLoopOnTimeout; extern UINT8 AcpiGbl_RegionFillValue; +extern INIT_FILE_ENTRY *AcpiGbl_InitEntries; +extern UINT32 AcpiGbl_InitFileLineCount; extern UINT8 AcpiGbl_UseHwReducedFadt; extern BOOLEAN AcpiGbl_DisplayRegionAccess; extern BOOLEAN AcpiGbl_DoInterfaceTests; @@ -331,13 +342,18 @@ AeOpenInitializationFile ( char *Filename); void -AeDoObjectOverrides ( +AeProcessInitFile ( void); ACPI_STATUS AeSetupConfiguration ( void *RegionAddr); +ACPI_STATUS +AeLookupInitFileEntry ( + char *Pathname, + UINT64 *Value); + /* aeexec */ void diff --git a/source/tools/acpiexec/aeinitfile.c b/source/tools/acpiexec/aeinitfile.c index 804a2cbcd6c9..79b797e094bc 100644 --- a/source/tools/acpiexec/aeinitfile.c +++ b/source/tools/acpiexec/aeinitfile.c @@ -159,10 +159,8 @@ /* Local prototypes */ static void -AeDoOneOverride ( - char *Pathname, - char *ValueString, - ACPI_OPERAND_OBJECT *ObjDesc, +AeEnterInitFileEntry ( + INIT_FILE_ENTRY InitEntry, ACPI_WALK_STATE *WalkState); @@ -206,13 +204,15 @@ AeOpenInitializationFile ( /****************************************************************************** * - * FUNCTION: AeDoObjectOverrides + * FUNCTION: AeProcessInitFile * * PARAMETERS: None * * RETURN: None * - * DESCRIPTION: Read the initialization file and perform all overrides + * DESCRIPTION: Read the initialization file and perform all namespace + * initializations. AcpiGbl_InitEntries will be used for region + * field initialization. * * NOTE: The format of the file is multiple lines, each of format: * @@ -220,12 +220,13 @@ AeOpenInitializationFile ( *****************************************************************************/ void -AeDoObjectOverrides ( +AeProcessInitFile( void) { - ACPI_OPERAND_OBJECT *ObjDesc; ACPI_WALK_STATE *WalkState; int i; + UINT64 idx; + ACPI_STATUS Status; if (!InitFile) @@ -235,13 +236,18 @@ AeDoObjectOverrides ( /* Create needed objects to be reused for each init entry */ - ObjDesc = AcpiUtCreateIntegerObject (0); WalkState = AcpiDsCreateWalkState (0, NULL, NULL, NULL); NameBuffer[0] = '\\'; - /* Read the entire file line-by-line */ - while (fgets (LineBuffer, AE_FILE_BUFFER_SIZE, InitFile) != NULL) + { + ++AcpiGbl_InitFileLineCount; + } + rewind (InitFile); + + AcpiGbl_InitEntries = + AcpiOsAllocate (sizeof (INIT_FILE_ENTRY) * AcpiGbl_InitFileLineCount); + for (idx = 0; fgets (LineBuffer, AE_FILE_BUFFER_SIZE, InitFile); ++idx) { if (sscanf (LineBuffer, "%s %s\n", &NameBuffer[1], ValueBuffer) != 2) @@ -257,7 +263,20 @@ AeDoObjectOverrides ( i = 1; } - AeDoOneOverride (&NameBuffer[i], ValueBuffer, ObjDesc, WalkState); + AcpiGbl_InitEntries[idx].Name = + AcpiOsAllocateZeroed (strnlen (NameBuffer + i, AE_FILE_BUFFER_SIZE) + 1); + + strcpy (AcpiGbl_InitEntries[idx].Name, NameBuffer + i); + + Status = AcpiUtStrtoul64 (ValueBuffer, &AcpiGbl_InitEntries[idx].Value); + if (ACPI_FAILURE (Status)) + { + AcpiOsPrintf ("%s %s\n", ValueBuffer, + AcpiFormatException (Status)); + goto CleanupAndExit; + } + + AeEnterInitFileEntry (AcpiGbl_InitEntries[idx], WalkState); } /* Cleanup */ @@ -265,77 +284,97 @@ AeDoObjectOverrides ( CleanupAndExit: fclose (InitFile); AcpiDsDeleteWalkState (WalkState); +} + + +/****************************************************************************** + * + * FUNCTION: AeInitFileEntry + * + * PARAMETERS: InitEntry - Entry of the init file + * WalkState - Used for the Store operation + * + * RETURN: None + * + * DESCRIPTION: Perform initialization of a single namespace object + * + * Note: namespace of objects are limited to integers and region + * fields units of 8 bytes at this time. + * + *****************************************************************************/ + +static void +AeEnterInitFileEntry ( + INIT_FILE_ENTRY InitEntry, + ACPI_WALK_STATE *WalkState) +{ + char *Pathname = InitEntry.Name; + UINT64 Value = InitEntry.Value; + ACPI_OPERAND_OBJECT *ObjDesc; + ACPI_NAMESPACE_NODE *NewNode; + ACPI_STATUS Status; + + + AcpiOsPrintf ("Initializing namespace element: %s\n", Pathname); + Status = AcpiNsLookup (NULL, Pathname, ACPI_TYPE_INTEGER, + ACPI_IMODE_LOAD_PASS2, ACPI_NS_ERROR_IF_FOUND | ACPI_NS_NO_UPSEARCH | + ACPI_NS_EARLY_INIT, NULL, &NewNode); + if (ACPI_FAILURE (Status)) + { + ACPI_EXCEPTION ((AE_INFO, Status, + "While creating name from namespace initialization file: %s", + Pathname)); + return; + } + + ObjDesc = AcpiUtCreateIntegerObject (Value); + + AcpiOsPrintf ("New value: 0x%8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (Value)); + + /* Store pointer to value descriptor in the Node */ + + Status = AcpiNsAttachObject (NewNode, ObjDesc, + ACPI_TYPE_INTEGER); + + /* Remove local reference to the object */ + AcpiUtRemoveReference (ObjDesc); } /****************************************************************************** * - * FUNCTION: AeDoOneOverride + * FUNCTION: AeLookupInitFileEntry * - * PARAMETERS: Pathname - AML namepath - * ValueString - New integer value to be stored - * ObjDesc - Descriptor with integer override value - * WalkState - Used for the Store operation + * PARAMETERS: Pathname - AML namepath in external format + * ValueString - value of the namepath if it exitst * * RETURN: None * - * DESCRIPTION: Perform an override for a single namespace object + * DESCRIPTION: Search the init file for a particular name and its value. * *****************************************************************************/ -static void -AeDoOneOverride ( +ACPI_STATUS +AeLookupInitFileEntry ( char *Pathname, - char *ValueString, - ACPI_OPERAND_OBJECT *ObjDesc, - ACPI_WALK_STATE *WalkState) + UINT64 *Value) { - ACPI_HANDLE Handle; - ACPI_STATUS Status; - UINT64 Value; + UINT32 i; - - AcpiOsPrintf ("Value Override: %s, ", Pathname); - - /* - * Get the namespace node associated with the override - * pathname from the init file. - */ - Status = AcpiGetHandle (NULL, Pathname, &Handle); - if (ACPI_FAILURE (Status)) + if (!AcpiGbl_InitEntries) { - AcpiOsPrintf ("%s\n", AcpiFormatException (Status)); - return; + return AE_NOT_FOUND; } - /* Extract the 64-bit integer */ - - Status = AcpiUtStrtoul64 (ValueString, &Value); - if (ACPI_FAILURE (Status)) + for (i = 0; i < AcpiGbl_InitFileLineCount; ++i) { - AcpiOsPrintf ("%s %s\n", ValueString, - AcpiFormatException (Status)); - return; + if (!strcmp(AcpiGbl_InitEntries[i].Name, Pathname)) + { + *Value = AcpiGbl_InitEntries[i].Value; + return AE_OK; + } } - - ObjDesc->Integer.Value = Value; - - /* - * At the point this function is called, the namespace is fully - * built and initialized. We can simply store the new object to - * the target node. - */ - AcpiExEnterInterpreter (); - Status = AcpiExStore (ObjDesc, Handle, WalkState); - AcpiExExitInterpreter (); - - if (ACPI_FAILURE (Status)) - { - AcpiOsPrintf ("%s\n", AcpiFormatException (Status)); - return; - } - - AcpiOsPrintf ("New value: 0x%8.8X%8.8X\n", - ACPI_FORMAT_UINT64 (Value)); + return AE_NOT_FOUND; } diff --git a/source/tools/acpiexec/aemain.c b/source/tools/acpiexec/aemain.c index 2db2cef5d8ed..b1781b565861 100644 --- a/source/tools/acpiexec/aemain.c +++ b/source/tools/acpiexec/aemain.c @@ -199,6 +199,8 @@ BOOLEAN AcpiGbl_LoadTestTables = FALSE; BOOLEAN AcpiGbl_AeLoadOnly = FALSE; static UINT8 AcpiGbl_ExecutionMode = AE_MODE_COMMAND_LOOP; static char BatchBuffer[AE_BUFFER_SIZE]; /* Batch command buffer */ +INIT_FILE_ENTRY *AcpiGbl_InitEntries = NULL; +UINT32 AcpiGbl_InitFileLineCount = 0; #define ACPIEXEC_NAME "AML Execution/Debug Utility" #define AE_SUPPORTED_OPTIONS "?b:d:e:f^ghlm^rt^v^:x:" @@ -677,6 +679,8 @@ main ( signal (SIGSEGV, AeSignalHandler); } + AeProcessInitFile(); + /* The remaining arguments are filenames for ACPI tables */ if (!argv[AcpiGbl_Optind]) @@ -785,12 +789,6 @@ main ( */ AeInstallLateHandlers (); - /* - * This call implements the "initialization file" option for AcpiExec. - * This is the precise point that we want to perform the overrides. - */ - AeDoObjectOverrides (); - /* Finish the ACPICA initialization */ Status = AcpiInitializeObjects (InitFlags); @@ -848,5 +846,6 @@ main ( ErrorExit: (void) AcpiTerminate (); AcDeleteTableList (ListHead); + AcpiOsFree (AcpiGbl_InitEntries); return (ExitCode); } diff --git a/source/tools/acpiexec/aeregion.c b/source/tools/acpiexec/aeregion.c index 56f939463c1e..5bad83591e65 100644 --- a/source/tools/acpiexec/aeregion.c +++ b/source/tools/acpiexec/aeregion.c @@ -234,9 +234,9 @@ AeRegionHandler ( SpaceId = RegionObject->Region.SpaceId; ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, - "Operation Region request on %s at 0x%X\n", + "Operation Region request on %s at 0x%X, BitWidth 0x%X, RegionLength 0x%X\n", AcpiUtGetRegionName (RegionObject->Region.SpaceId), - (UINT32) Address)); + (UINT32) Address, BitWidth, (UINT32) Length)); /* * Region support can be disabled with the -do option. @@ -410,7 +410,7 @@ AeRegionHandler ( if (AcpiGbl_DisplayRegionAccess) { AcpiOsPrintf ("AcpiExec: %s " - "%s: Attr %X Addr %.4X BaseAddr %.4X Len %.2X Width %X BufLen %X", + "%s: Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X", AcpiUtGetRegionName (SpaceId), (Function & ACPI_IO_MASK) ? "Write" : "Read ", (UINT32) (Function >> 16), @@ -424,7 +424,7 @@ AeRegionHandler ( Status = AcpiBufferToResource (MyContext->Connection, MyContext->Length, &Resource); - AcpiOsPrintf (" [AccLen %.2X Conn %p]", + AcpiOsPrintf (" [AccessLength %.2X Connnection %p]", MyContext->AccessLength, MyContext->Connection); } AcpiOsPrintf ("\n"); From abe858c1d6e8bf7d7206b09ed715677ac63b5fca Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Fri, 10 Aug 2018 20:05:41 +0000 Subject: [PATCH 057/322] Import lua 5.3.5 --- README | 2 +- doc/contents.html | 6 +-- doc/lua.css | 33 +++++++-------- doc/manual.html | 101 ++++++++++++++++++++++------------------------ doc/readme.html | 6 +-- src/Makefile | 4 +- src/lapi.c | 7 ++-- src/lapi.h | 2 +- src/lauxlib.c | 2 +- src/lauxlib.h | 2 +- src/lbaselib.c | 2 +- src/lbitlib.c | 2 +- src/lcode.c | 2 +- src/lcode.h | 2 +- src/lcorolib.c | 2 +- src/lctype.c | 2 +- src/lctype.h | 2 +- src/ldblib.c | 2 +- src/ldebug.c | 3 +- src/ldebug.h | 2 +- src/ldo.c | 2 +- src/ldo.h | 2 +- src/ldump.c | 2 +- src/lfunc.c | 2 +- src/lfunc.h | 2 +- src/lgc.c | 5 ++- src/lgc.h | 2 +- src/linit.c | 2 +- src/liolib.c | 13 ++++-- src/llex.c | 2 +- src/llex.h | 2 +- src/llimits.h | 2 +- src/lmathlib.c | 2 +- src/lmem.c | 2 +- src/lmem.h | 2 +- src/loadlib.c | 2 +- src/lobject.c | 5 ++- src/lobject.h | 2 +- src/lopcodes.c | 2 +- src/lopcodes.h | 2 +- src/loslib.c | 8 ++-- src/lparser.c | 4 +- src/lparser.h | 2 +- src/lprefix.h | 2 +- src/lstate.c | 2 +- src/lstate.h | 20 ++++++++- src/lstring.c | 2 +- src/lstring.h | 2 +- src/lstrlib.c | 8 ++-- src/ltable.c | 35 ++++++++++++---- src/ltable.h | 4 +- src/ltablib.c | 2 +- src/ltm.c | 2 +- src/ltm.h | 2 +- src/lua.c | 4 +- src/lua.h | 8 ++-- src/luac.c | 5 ++- src/luaconf.h | 9 ++++- src/lualib.h | 2 +- src/lundump.c | 2 +- src/lundump.h | 2 +- src/lutf8lib.c | 4 +- src/lvm.c | 2 +- src/lvm.h | 2 +- src/lzio.c | 2 +- src/lzio.h | 2 +- 66 files changed, 216 insertions(+), 166 deletions(-) diff --git a/README b/README index 0b31908a06cb..ed424defe025 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -This is Lua 5.3.4, released on 12 Jan 2017. +This is Lua 5.3.5, released on 26 Jun 2018. For installation instructions, license details, and further information about Lua, see doc/readme.html. diff --git a/doc/contents.html b/doc/contents.html index 445556f96415..c4eb2677906d 100644 --- a/doc/contents.html +++ b/doc/contents.html @@ -32,7 +32,7 @@ For a complete introduction to Lua programming, see the book

-Copyright © 2015–2017 Lua.org, PUC-Rio. +Copyright © 2015–2018 Lua.org, PUC-Rio. Freely available under the terms of the Lua license. @@ -609,10 +609,10 @@ Freely available under the terms of the

diff --git a/doc/lua.css b/doc/lua.css index 5bedf7eb898e..cbd0799d1525 100644 --- a/doc/lua.css +++ b/doc/lua.css @@ -10,7 +10,7 @@ body { line-height: 1.25 ; margin: 16px auto ; padding: 32px ; - border: solid #a0a0a0 1px ; + border: solid #ccc 1px ; border-radius: 20px ; max-width: 70em ; width: 90% ; @@ -111,36 +111,29 @@ pre.session { border-radius: 8px ; } -td.gutter { - width: 4% ; -} - -table.columns { +table { border: none ; border-spacing: 0 ; border-collapse: collapse ; } +td { + padding: 0 ; + margin: 0 ; +} + +td.gutter { + width: 4% ; +} + table.columns td { vertical-align: top ; - padding: 0 ; padding-bottom: 1em ; text-align: justify ; line-height: 1.25 ; } -p.logos a:link:hover, p.logos a:visited:hover { - background-color: inherit ; -} - -table.book { - border: none ; - border-spacing: 0 ; - border-collapse: collapse ; -} - table.book td { - padding: 0 ; vertical-align: top ; } @@ -159,6 +152,10 @@ table.book span { margin-top: 0.25em ; } +p.logos a:link:hover, p.logos a:visited:hover { + background-color: inherit ; +} + img { background-color: white ; } diff --git a/doc/manual.html b/doc/manual.html index 3126b5d6afb3..89a642a45d92 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

-Copyright © 2015–2017 Lua.org, PUC-Rio. +Copyright © 2015–2018 Lua.org, PUC-Rio. Freely available under the terms of the Lua license. @@ -35,7 +35,7 @@ Freely available under the terms of the

- + @@ -203,8 +203,8 @@ even those that do not support threads natively.

The type table implements associative arrays, -that is, arrays that can be indexed not only with numbers, -but with any Lua value except nil and NaN. +that is, arrays that can have as indices not only numbers, +but any Lua value except nil and NaN. (Not a Number is a special value used to represent undefined or unrepresentable numerical results, such as 0/0.) Tables can be heterogeneous; @@ -400,6 +400,8 @@ with the event name prefixed by two underscores; the corresponding values are called metamethods. In the previous example, the key is "__add" and the metamethod is the function that performs the addition. +Unless stated otherwise, +metamethods should be function values.

@@ -597,7 +599,7 @@ it is also slower than a real __le metamethod.)

  • __index: -The indexing access table[key]. +The indexing access operation table[key]. This event happens when table is not a table or when key is not present in table. The metamethod is looked up in table. @@ -1276,13 +1278,8 @@ Square brackets are used to index a table:
     	var ::= prefixexp ‘[’ exp ‘]

    -The meaning of accesses to table fields can be changed via metatables. -An access to an indexed variable t[i] is equivalent to -a call gettable_event(t,i). -(See §2.4 for a complete description of the -gettable_event function. -This function is not defined or callable in Lua. -We use it here only for explanatory purposes.) +The meaning of accesses to table fields can be changed via metatables +(see §2.4).

    @@ -1476,23 +1473,18 @@ and cyclically permutes the values of x, y, and z. -

    -The meaning of assignments to global variables -and table fields can be changed via metatables. -An assignment to an indexed variable t[i] = val is equivalent to -settable_event(t,i,val). -(See §2.4 for a complete description of the -settable_event function. -This function is not defined or callable in Lua. -We use it here only for explanatory purposes.) - -

    An assignment to a global name x = val is equivalent to the assignment _ENV.x = val (see §2.2). +

    +The meaning of assignments to table fields and +global variables (which are actually table fields, too) +can be changed via metatables (see §2.4). + + @@ -1831,17 +1823,17 @@ Here are some examples: g(f(), x) -- f() is adjusted to 1 result g(x, f()) -- g gets x plus all results from f() a,b,c = f(), x -- f() is adjusted to 1 result (c gets nil) - a,b = ... -- a gets the first vararg parameter, b gets + a,b = ... -- a gets the first vararg argument, b gets -- the second (both a and b can get nil if there - -- is no corresponding vararg parameter) + -- is no corresponding vararg argument) a,b,c = x, f() -- f() is adjusted to 2 results a,b,c = f() -- f() is adjusted to 3 results return f() -- returns all results from f() - return ... -- returns all received vararg parameters + return ... -- returns all received vararg arguments return x,y,f() -- returns x, y, and all results from f() {f()} -- creates a list with all results from f() - {...} -- creates a list with all vararg parameters + {...} -- creates a list with all vararg arguments {f(), nil} -- f() is adjusted to 1 result @@ -2039,9 +2031,12 @@ two objects are considered equal only if they are the same object. Every time you create a new object (a table, userdata, or thread), this new object is different from any previously existing object. -Closures with the same reference are always equal. +A closure is always equal to itself. Closures with any detectable difference (different behavior, different definition) are always different. +Closures created at different times but with no detectable differences +may be classified as equal or not +(depending on internal caching details).

    @@ -2303,7 +2298,7 @@ If the value of prefixexp has type function, then this function is called with the given arguments. Otherwise, the prefixexp "call" metamethod is called, -having as first parameter the value of prefixexp, +having as first argument the value of prefixexp, followed by the original call arguments (see §2.4). @@ -2881,7 +2876,7 @@ it can do whatever it wants on that Lua state, as it should be already protected. However, when C code operates on other Lua states -(e.g., a Lua parameter to the function, +(e.g., a Lua argument to the function, a Lua state stored in the registry, or the result of lua_newthread), it should use them only in API calls that cannot raise errors. @@ -3370,7 +3365,7 @@ it is left unchanged. Destroys all objects in the given Lua state (calling the corresponding garbage-collection metamethods, if any) and frees all dynamic memory used by this state. -On several platforms, you may not need to call this function, +In several platforms, you may not need to call this function, because all resources are naturally released when the host program ends. On the other hand, long-running programs that create multiple states, such as daemons or web servers, @@ -5584,7 +5579,7 @@ given as argument to a hook (see lua_Hook).

    -To get information about a function you push it onto the stack +To get information about a function, you push it onto the stack and start the what string with the character '>'. (In that case, lua_getinfo pops the function from the top of the stack.) @@ -6462,7 +6457,7 @@ file-related functions in the standard library

    Pushes onto the stack the field e from the metatable -of the object at index obj and returns the type of pushed value. +of the object at index obj and returns the type of the pushed value. If the object does not have a metatable, or if the metatable does not have this field, pushes nothing and returns LUA_TNIL. @@ -6749,7 +6744,7 @@ In words, if the argument arg is nil or absent, the macro results in the default dflt. Otherwise, it results in the result of calling func with the state L and the argument index arg as -parameters. +arguments. Note that it evaluates the expression dflt only if needed. @@ -8680,7 +8675,7 @@ the lowercase letters plus the '-' character.

    You can put a closing square bracket in a set by positioning it as the first character in the set. -You can put an hyphen in a set +You can put a hyphen in a set by positioning it as the first or the last character in the set. (You can also use an escape for both cases.) @@ -9082,8 +9077,8 @@ Returns the destination table a2.

    -Returns a new table with all parameters stored into keys 1, 2, etc. -and with a field "n" with the total number of parameters. +Returns a new table with all arguments stored into keys 1, 2, etc. +and with a field "n" with the total number of arguments. Note that the resulting table may not be a sequence. @@ -9215,7 +9210,7 @@ Returns the arc sine of x (in radians).

    Returns the arc tangent of y/x (in radians), -but uses the signs of both parameters to find the +but uses the signs of both arguments to find the quadrant of the result. (It also handles correctly the case of x being zero.) @@ -9516,7 +9511,7 @@ all I/O functions return nil on failure (plus an error message as a second result and a system-dependent error code as a third result) and some value different from nil on success. -On non-POSIX systems, +In non-POSIX systems, the computation of the error message and error code in case of errors may be not thread safe, @@ -9553,7 +9548,7 @@ When called with a file name, it opens the named file (in text mode), and sets its handle as the default input file. When called with a file handle, it simply sets this file handle as the default input file. -When called without parameters, +When called without arguments, it returns the current default input file. @@ -9580,7 +9575,7 @@ it returns no values (to finish the loop) and automatically closes the file. The call io.lines() (with no file name) is equivalent to io.input():lines("*l"); that is, it iterates over the lines of the default input file. -In this case it does not close the file when the loop ends. +In this case, the iterator does not close the file when the loop ends.

    @@ -9963,7 +9958,7 @@ the host system and on the current locale.

    -On non-POSIX systems, +In non-POSIX systems, this function may be not thread safe because of its reliance on C function gmtime and C function localtime. @@ -10163,7 +10158,7 @@ and explicitly removed when no longer needed.

    -On POSIX systems, +In POSIX systems, this function also creates a file with that name, to avoid security risks. (Someone else might create the file with wrong permissions @@ -10301,8 +10296,8 @@ The first parameter or local variable has index 1, and so on, following the order that they are declared in the code, counting only the variables that are active in the current scope of the function. -Negative indices refer to vararg parameters; --1 is the first vararg parameter. +Negative indices refer to vararg arguments; +-1 is the first vararg argument. The function returns nil if there is no variable with the given index, and raises an error when called with a level out of range. (You can call debug.getinfo to check whether the level is valid.) @@ -10400,7 +10395,7 @@ When called without arguments,

    -When the hook is called, its first parameter is a string +When the hook is called, its first argument is a string describing the event that has triggered its call: "call" (or "tail call"), "return", @@ -10551,7 +10546,8 @@ The options are: