xref: /freebsd-src/sys/contrib/openzfs/lib/libzfsbootenv/lzbe_device.c (revision 2a58b312b62f908ec92311d1bd8536dbaeb8e55b)
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
lzbe_set_boot_device(const char * pool,lzbe_flags_t flag,const char * device)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 		}
66c03c5b1cSMartin Matuska 		zfs_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 
77be181ee2SMartin Matuska 	rv = 0;
782c48331dSMatt Macy 	/*
792c48331dSMatt Macy 	 * If device name is empty, remove boot device configuration.
802c48331dSMatt Macy 	 */
812c48331dSMatt Macy 	if ((device == NULL || *device == '\0')) {
822c48331dSMatt Macy 		if (nvlist_exists(nv, OS_BOOTONCE))
832c48331dSMatt Macy 			fnvlist_remove(nv, OS_BOOTONCE);
842c48331dSMatt Macy 	} else {
852c48331dSMatt Macy 		/*
862c48331dSMatt Macy 		 * Use device name directly if it does start with
8716038816SMartin Matuska 		 * prefix "zfs:". Otherwise, add prefix and suffix.
882c48331dSMatt Macy 		 */
892c48331dSMatt Macy 		if (strncmp(device, "zfs:", 4) == 0) {
902c48331dSMatt Macy 			fnvlist_add_string(nv, OS_BOOTONCE, device);
912c48331dSMatt Macy 		} else {
9216038816SMartin Matuska 			if (asprintf(&descriptor, "zfs:%s:", device) > 0) {
932c48331dSMatt Macy 				fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
942c48331dSMatt Macy 				free(descriptor);
9516038816SMartin Matuska 			} else
9616038816SMartin Matuska 				rv = ENOMEM;
972c48331dSMatt Macy 		}
982c48331dSMatt Macy 	}
99be181ee2SMartin Matuska 	if (rv == 0)
1002c48331dSMatt Macy 		rv = zpool_set_bootenv(zphdl, nv);
1012c48331dSMatt Macy 	if (rv != 0)
1022c48331dSMatt Macy 		fprintf(stderr, "%s\n", libzfs_error_description(hdl));
1032c48331dSMatt Macy 
1042c48331dSMatt Macy 	fnvlist_free(nv);
1052c48331dSMatt Macy 	zpool_close(zphdl);
1062c48331dSMatt Macy 	libzfs_fini(hdl);
1072c48331dSMatt Macy 	return (rv);
1082c48331dSMatt Macy }
1092c48331dSMatt Macy 
1102c48331dSMatt Macy /*
1112c48331dSMatt Macy  * Return boot device name from bootenv, if set.
1122c48331dSMatt Macy  */
1132c48331dSMatt Macy int
lzbe_get_boot_device(const char * pool,char ** device)1142c48331dSMatt Macy lzbe_get_boot_device(const char *pool, char **device)
1152c48331dSMatt Macy {
1162c48331dSMatt Macy 	libzfs_handle_t *hdl;
1172c48331dSMatt Macy 	zpool_handle_t *zphdl;
1182c48331dSMatt Macy 	nvlist_t *nv;
119*2a58b312SMartin Matuska 	const char *val;
1202c48331dSMatt Macy 	int rv = -1;
1212c48331dSMatt Macy 
1222c48331dSMatt Macy 	if (pool == NULL || *pool == '\0' || device == NULL)
1232c48331dSMatt Macy 		return (rv);
1242c48331dSMatt Macy 
1252c48331dSMatt Macy 	if ((hdl = libzfs_init()) == NULL)
1262c48331dSMatt Macy 		return (rv);
1272c48331dSMatt Macy 
1282c48331dSMatt Macy 	zphdl = zpool_open(hdl, pool);
1292c48331dSMatt Macy 	if (zphdl == NULL) {
1302c48331dSMatt Macy 		libzfs_fini(hdl);
1312c48331dSMatt Macy 		return (rv);
1322c48331dSMatt Macy 	}
1332c48331dSMatt Macy 
1342c48331dSMatt Macy 	rv = zpool_get_bootenv(zphdl, &nv);
1352c48331dSMatt Macy 	if (rv == 0) {
1362c48331dSMatt Macy 		rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
1372c48331dSMatt Macy 		if (rv == 0) {
1382c48331dSMatt Macy 			/*
1392c48331dSMatt Macy 			 * zfs device descriptor is in form of "zfs:dataset:",
1402c48331dSMatt Macy 			 * we only do need dataset name.
1412c48331dSMatt Macy 			 */
1422c48331dSMatt Macy 			if (strncmp(val, "zfs:", 4) == 0) {
143*2a58b312SMartin Matuska 				char *tmp = strdup(val + 4);
144*2a58b312SMartin Matuska 				if (tmp != NULL) {
145*2a58b312SMartin Matuska 					size_t len = strlen(tmp);
1462c48331dSMatt Macy 
147*2a58b312SMartin Matuska 					if (tmp[len - 1] == ':')
148*2a58b312SMartin Matuska 						tmp[len - 1] = '\0';
149*2a58b312SMartin Matuska 					*device = tmp;
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