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