xref: /freebsd/sys/contrib/openzfs/lib/libzfsbootenv/lzbe_device.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
22c48331dSMatt Macy /*
32c48331dSMatt Macy  * This file and its contents are supplied under the terms of the
42c48331dSMatt Macy  * Common Development and Distribution License ("CDDL"), version 1.0.
52c48331dSMatt Macy  * You may only use this file in accordance with the terms of version
62c48331dSMatt Macy  * 1.0 of the CDDL.
72c48331dSMatt Macy  *
82c48331dSMatt Macy  * A full copy of the text of the CDDL should have accompanied this
92c48331dSMatt Macy  * source.  A copy of the CDDL is also available via the Internet at
102c48331dSMatt Macy  * http://www.illumos.org/license/CDDL.
112c48331dSMatt Macy  */
122c48331dSMatt Macy /*
132c48331dSMatt Macy  * Copyright 2020 Toomas Soome <tsoome@me.com>
142c48331dSMatt Macy  */
152c48331dSMatt Macy 
162c48331dSMatt Macy #include <sys/types.h>
172c48331dSMatt Macy #include <string.h>
182c48331dSMatt Macy #include <libzfs.h>
192c48331dSMatt Macy #include <libzfsbootenv.h>
202c48331dSMatt Macy #include <sys/zfs_bootenv.h>
212c48331dSMatt Macy #include <sys/vdev_impl.h>
222c48331dSMatt Macy 
232c48331dSMatt Macy /*
242c48331dSMatt Macy  * Store device name to zpool label bootenv area.
252c48331dSMatt Macy  * This call will set bootenv version to VB_NVLIST, if bootenv currently
262c48331dSMatt Macy  * does contain other version, then old data will be replaced.
272c48331dSMatt Macy  */
282c48331dSMatt Macy int
lzbe_set_boot_device(const char * pool,lzbe_flags_t flag,const char * device)292c48331dSMatt Macy lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device)
302c48331dSMatt Macy {
312c48331dSMatt Macy 	libzfs_handle_t *hdl;
322c48331dSMatt Macy 	zpool_handle_t *zphdl;
332c48331dSMatt Macy 	nvlist_t *nv;
342c48331dSMatt Macy 	char *descriptor;
352c48331dSMatt Macy 	uint64_t version;
362c48331dSMatt Macy 	int rv = -1;
372c48331dSMatt Macy 
382c48331dSMatt Macy 	if (pool == NULL || *pool == '\0')
392c48331dSMatt Macy 		return (rv);
402c48331dSMatt Macy 
412c48331dSMatt Macy 	if ((hdl = libzfs_init()) == NULL)
422c48331dSMatt Macy 		return (rv);
432c48331dSMatt Macy 
442c48331dSMatt Macy 	zphdl = zpool_open(hdl, pool);
452c48331dSMatt Macy 	if (zphdl == NULL) {
462c48331dSMatt Macy 		libzfs_fini(hdl);
472c48331dSMatt Macy 		return (rv);
482c48331dSMatt Macy 	}
492c48331dSMatt Macy 
502c48331dSMatt Macy 	switch (flag) {
512c48331dSMatt Macy 	case lzbe_add:
522c48331dSMatt Macy 		rv = zpool_get_bootenv(zphdl, &nv);
532c48331dSMatt Macy 		if (rv == 0) {
542c48331dSMatt Macy 			/*
552c48331dSMatt Macy 			 * We got the nvlist, check for version.
562c48331dSMatt Macy 			 * if version is missing or is not VB_NVLIST,
572c48331dSMatt Macy 			 * create new list.
582c48331dSMatt Macy 			 */
592c48331dSMatt Macy 			rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION,
602c48331dSMatt Macy 			    &version);
612c48331dSMatt Macy 			if (rv == 0 && version == VB_NVLIST)
622c48331dSMatt Macy 				break;
632c48331dSMatt Macy 
642c48331dSMatt Macy 			/* Drop this nvlist */
652c48331dSMatt Macy 			fnvlist_free(nv);
662c48331dSMatt Macy 		}
67c03c5b1cSMartin Matuska 		zfs_fallthrough;
682c48331dSMatt Macy 	case lzbe_replace:
692c48331dSMatt Macy 		nv = fnvlist_alloc();
702c48331dSMatt Macy 		break;
712c48331dSMatt Macy 	default:
722c48331dSMatt Macy 		return (rv);
732c48331dSMatt Macy 	}
742c48331dSMatt Macy 
752c48331dSMatt Macy 	/* version is mandatory */
762c48331dSMatt Macy 	fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST);
772c48331dSMatt Macy 
78be181ee2SMartin Matuska 	rv = 0;
792c48331dSMatt Macy 	/*
802c48331dSMatt Macy 	 * If device name is empty, remove boot device configuration.
812c48331dSMatt Macy 	 */
822c48331dSMatt Macy 	if ((device == NULL || *device == '\0')) {
832c48331dSMatt Macy 		if (nvlist_exists(nv, OS_BOOTONCE))
842c48331dSMatt Macy 			fnvlist_remove(nv, OS_BOOTONCE);
852c48331dSMatt Macy 	} else {
862c48331dSMatt Macy 		/*
872c48331dSMatt Macy 		 * Use device name directly if it does start with
8816038816SMartin Matuska 		 * prefix "zfs:". Otherwise, add prefix and suffix.
892c48331dSMatt Macy 		 */
902c48331dSMatt Macy 		if (strncmp(device, "zfs:", 4) == 0) {
912c48331dSMatt Macy 			fnvlist_add_string(nv, OS_BOOTONCE, device);
922c48331dSMatt Macy 		} else {
9316038816SMartin Matuska 			if (asprintf(&descriptor, "zfs:%s:", device) > 0) {
942c48331dSMatt Macy 				fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
952c48331dSMatt Macy 				free(descriptor);
9616038816SMartin Matuska 			} else
9716038816SMartin Matuska 				rv = ENOMEM;
982c48331dSMatt Macy 		}
992c48331dSMatt Macy 	}
100be181ee2SMartin Matuska 	if (rv == 0)
1012c48331dSMatt Macy 		rv = zpool_set_bootenv(zphdl, nv);
1022c48331dSMatt Macy 	if (rv != 0)
1032c48331dSMatt Macy 		fprintf(stderr, "%s\n", libzfs_error_description(hdl));
1042c48331dSMatt Macy 
1052c48331dSMatt Macy 	fnvlist_free(nv);
1062c48331dSMatt Macy 	zpool_close(zphdl);
1072c48331dSMatt Macy 	libzfs_fini(hdl);
1082c48331dSMatt Macy 	return (rv);
1092c48331dSMatt Macy }
1102c48331dSMatt Macy 
1112c48331dSMatt Macy /*
1122c48331dSMatt Macy  * Return boot device name from bootenv, if set.
1132c48331dSMatt Macy  */
1142c48331dSMatt Macy int
lzbe_get_boot_device(const char * pool,char ** device)1152c48331dSMatt Macy lzbe_get_boot_device(const char *pool, char **device)
1162c48331dSMatt Macy {
1172c48331dSMatt Macy 	libzfs_handle_t *hdl;
1182c48331dSMatt Macy 	zpool_handle_t *zphdl;
1192c48331dSMatt Macy 	nvlist_t *nv;
1202a58b312SMartin Matuska 	const char *val;
1212c48331dSMatt Macy 	int rv = -1;
1222c48331dSMatt Macy 
1232c48331dSMatt Macy 	if (pool == NULL || *pool == '\0' || device == NULL)
1242c48331dSMatt Macy 		return (rv);
1252c48331dSMatt Macy 
1262c48331dSMatt Macy 	if ((hdl = libzfs_init()) == NULL)
1272c48331dSMatt Macy 		return (rv);
1282c48331dSMatt Macy 
1292c48331dSMatt Macy 	zphdl = zpool_open(hdl, pool);
1302c48331dSMatt Macy 	if (zphdl == NULL) {
1312c48331dSMatt Macy 		libzfs_fini(hdl);
1322c48331dSMatt Macy 		return (rv);
1332c48331dSMatt Macy 	}
1342c48331dSMatt Macy 
1352c48331dSMatt Macy 	rv = zpool_get_bootenv(zphdl, &nv);
1362c48331dSMatt Macy 	if (rv == 0) {
1372c48331dSMatt Macy 		rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
1382c48331dSMatt Macy 		if (rv == 0) {
1392c48331dSMatt Macy 			/*
1402c48331dSMatt Macy 			 * zfs device descriptor is in form of "zfs:dataset:",
1412c48331dSMatt Macy 			 * we only do need dataset name.
1422c48331dSMatt Macy 			 */
1432c48331dSMatt Macy 			if (strncmp(val, "zfs:", 4) == 0) {
1442a58b312SMartin Matuska 				char *tmp = strdup(val + 4);
1452a58b312SMartin Matuska 				if (tmp != NULL) {
1462a58b312SMartin Matuska 					size_t len = strlen(tmp);
1472c48331dSMatt Macy 
1482a58b312SMartin Matuska 					if (tmp[len - 1] == ':')
1492a58b312SMartin Matuska 						tmp[len - 1] = '\0';
1502a58b312SMartin Matuska 					*device = tmp;
1512c48331dSMatt Macy 				} else {
1522c48331dSMatt Macy 					rv = ENOMEM;
1532c48331dSMatt Macy 				}
1542c48331dSMatt Macy 			} else {
1552c48331dSMatt Macy 				rv = EINVAL;
1562c48331dSMatt Macy 			}
1572c48331dSMatt Macy 		}
1582c48331dSMatt Macy 		nvlist_free(nv);
1592c48331dSMatt Macy 	}
1602c48331dSMatt Macy 
1612c48331dSMatt Macy 	zpool_close(zphdl);
1622c48331dSMatt Macy 	libzfs_fini(hdl);
1632c48331dSMatt Macy 	return (rv);
1642c48331dSMatt Macy }
165