xref: /freebsd/lib/libbe/be_info.c (revision eb81f38a62c9ae246955feceedb8c043e78f871f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5  * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include "be.h"
34 #include "be_impl.h"
35 
36 static int snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data);
37 
38 /*
39  * Returns the name of the active boot environment
40  */
41 const char *
42 be_active_name(libbe_handle_t *lbh)
43 {
44 
45 	return (strrchr(lbh->rootfs, '/') + sizeof(char));
46 }
47 
48 
49 /*
50  * Returns full path of the active boot environment
51  */
52 const char *
53 be_active_path(libbe_handle_t *lbh)
54 {
55 
56 	return (lbh->rootfs);
57 }
58 
59 /*
60  * Returns the name of the next active boot environment
61  */
62 const char *
63 be_nextboot_name(libbe_handle_t *lbh)
64 {
65 
66 	return (strrchr(lbh->bootfs, '/') + sizeof(char));
67 }
68 
69 
70 /*
71  * Returns full path of the active boot environment
72  */
73 const char *
74 be_nextboot_path(libbe_handle_t *lbh)
75 {
76 
77 	return (lbh->bootfs);
78 }
79 
80 
81 /*
82  * Returns the path of the boot environment root dataset
83  */
84 const char *
85 be_root_path(libbe_handle_t *lbh)
86 {
87 
88 	return (lbh->root);
89 }
90 
91 
92 /*
93  * Populates dsnvl with one nvlist per bootenv dataset describing the properties
94  * of that dataset that we've declared ourselves to care about.
95  */
96 int
97 be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl)
98 {
99 	prop_data_t data;
100 
101 	data.lbh = lbh;
102 	data.list = dsnvl;
103 	data.single_object = false;
104 	return (be_proplist_update(&data));
105 }
106 
107 int
108 be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props)
109 {
110 	zfs_handle_t *snap_hdl;
111 	prop_data_t data;
112 	int ret;
113 
114 	data.lbh = lbh;
115 	data.list = props;
116 	data.single_object = true;
117 	if ((snap_hdl = zfs_open(lbh->lzh, name,
118 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL)
119 		return (BE_ERR_ZFSOPEN);
120 
121 	ret = prop_list_builder_cb(snap_hdl, &data);
122 	zfs_close(snap_hdl);
123 	return (ret);
124 }
125 
126 int
127 be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props)
128 {
129 	zfs_handle_t *ds_hdl;
130 	prop_data_t data;
131 	int ret;
132 
133 	data.lbh = lbh;
134 	data.list = props;
135 	data.single_object = false;
136 	if ((ds_hdl = zfs_open(lbh->lzh, name,
137 	    ZFS_TYPE_FILESYSTEM)) == NULL)
138 		return (BE_ERR_ZFSOPEN);
139 
140 	ret = snapshot_proplist_update(ds_hdl, &data);
141 	zfs_close(ds_hdl);
142 	return (ret);
143 }
144 
145 /*
146  * Internal callback function used by zfs_iter_filesystems. For each dataset in
147  * the bootenv root, populate an nvlist_t of its relevant properties.
148  */
149 int
150 prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p)
151 {
152 	char buf[512], *mountpoint;
153 	prop_data_t *data;
154 	libbe_handle_t *lbh;
155 	nvlist_t *props;
156 	const char *dataset, *name;
157 	boolean_t mounted;
158 
159 	/*
160 	 * XXX TODO:
161 	 *      some system for defining constants for the nvlist keys
162 	 *      error checking
163 	 */
164 	data = (prop_data_t *)data_p;
165 	lbh = data->lbh;
166 
167 	if (data->single_object)
168 		props = data->list;
169 	else
170 		nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
171 
172 	dataset = zfs_get_name(zfs_hdl);
173 	nvlist_add_string(props, "dataset", dataset);
174 
175 	name = strrchr(dataset, '/') + 1;
176 	nvlist_add_string(props, "name", name);
177 
178 	mounted = zfs_is_mounted(zfs_hdl, &mountpoint);
179 
180 	if (mounted)
181 		nvlist_add_string(props, "mounted", mountpoint);
182 
183 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512,
184 	    NULL, NULL, 0, 1) == 0)
185 		nvlist_add_string(props, "mountpoint", buf);
186 
187 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512,
188 	    NULL, NULL, 0, 1) == 0)
189 		nvlist_add_string(props, "origin", buf);
190 
191 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512,
192 	    NULL, NULL, 0, 1) == 0)
193 		nvlist_add_string(props, "creation", buf);
194 
195 	nvlist_add_boolean_value(props, "active",
196 	    (strcmp(be_active_path(lbh), dataset) == 0));
197 
198 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512,
199 	    NULL, NULL, 0, 1) == 0)
200 		nvlist_add_string(props, "used", buf);
201 
202 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512,
203 	    NULL, NULL, 0, 1) == 0)
204 		nvlist_add_string(props, "usedds", buf);
205 
206 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512,
207 	    NULL, NULL, 0, 1) == 0)
208 		nvlist_add_string(props, "usedsnap", buf);
209 
210 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512,
211 	    NULL, NULL, 0, 1) == 0)
212 		nvlist_add_string(props, "usedrefreserv", buf);
213 
214 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512,
215 	    NULL, NULL, 0, 1) == 0)
216 		nvlist_add_string(props, "referenced", buf);
217 
218 	nvlist_add_boolean_value(props, "nextboot",
219 	    (strcmp(be_nextboot_path(lbh), dataset) == 0));
220 
221 	if (!data->single_object)
222 		nvlist_add_nvlist(data->list, name, props);
223 
224 	return (0);
225 }
226 
227 
228 /*
229  * Updates the properties of each bootenv in the libbe handle
230  * XXX TODO: ensure that this is always consistent (run after adds, deletes,
231  *       renames,etc
232  */
233 int
234 be_proplist_update(prop_data_t *data)
235 {
236 	zfs_handle_t *root_hdl;
237 
238 	if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root,
239 	    ZFS_TYPE_FILESYSTEM)) == NULL)
240 		return (BE_ERR_ZFSOPEN);
241 
242 	/* XXX TODO: some error checking here */
243 	zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data);
244 
245 	zfs_close(root_hdl);
246 
247 	return (0);
248 }
249 
250 static int
251 snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data)
252 {
253 
254 	return (zfs_iter_snapshots_sorted(hdl, prop_list_builder_cb, data));
255 }
256 
257 
258 int
259 be_prop_list_alloc(nvlist_t **be_list)
260 {
261 
262 	return (nvlist_alloc(be_list, NV_UNIQUE_NAME, KM_SLEEP));
263 }
264 
265 /*
266  * frees property list and its children
267  */
268 void
269 be_prop_list_free(nvlist_t *be_list)
270 {
271 	nvlist_t *prop_list;
272 	nvpair_t *be_pair;
273 
274 	be_pair = nvlist_next_nvpair(be_list, NULL);
275 	if (nvpair_value_nvlist(be_pair, &prop_list) == 0)
276 		nvlist_free(prop_list);
277 
278 	while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) {
279 		if (nvpair_value_nvlist(be_pair, &prop_list) == 0)
280 			nvlist_free(prop_list);
281 	}
282 }
283 
284 
285 /*
286  * Usage
287  */
288 int
289 be_exists(libbe_handle_t *lbh, char *be)
290 {
291 	char buf[BE_MAXPATHLEN];
292 	nvlist_t *dsprops;
293 	char *mntpoint;
294 	bool valid;
295 
296 	be_root_concat(lbh, be, buf);
297 
298 	if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET))
299 		return (BE_ERR_NOENT);
300 
301 	/* Also check if it's mounted at / */
302 	if (be_prop_list_alloc(&dsprops) != 0)
303 		return (BE_ERR_UNKNOWN);
304 
305 	if (be_get_dataset_props(lbh, buf, dsprops) != 0) {
306 		nvlist_free(dsprops);
307 		return (BE_ERR_UNKNOWN);
308 	}
309 
310 	if (nvlist_lookup_string(dsprops, "mountpoint", &mntpoint) == 0) {
311 		valid = (strcmp(mntpoint, "/") == 0);
312 		nvlist_free(dsprops);
313 		return (valid ? BE_ERR_SUCCESS : BE_ERR_BADMOUNT);
314 	}
315 
316 	nvlist_free(dsprops);
317 	return (BE_ERR_BADMOUNT);
318 }
319