xref: /freebsd-src/sys/contrib/openzfs/lib/libzfsbootenv/lzbe_device.c (revision 2c48331d28f16c0efce5a72a81e7d71668c4a158)
1*2c48331dSMatt Macy /*
2*2c48331dSMatt Macy  * This file and its contents are supplied under the terms of the
3*2c48331dSMatt Macy  * Common Development and Distribution License ("CDDL"), version 1.0.
4*2c48331dSMatt Macy  * You may only use this file in accordance with the terms of version
5*2c48331dSMatt Macy  * 1.0 of the CDDL.
6*2c48331dSMatt Macy  *
7*2c48331dSMatt Macy  * A full copy of the text of the CDDL should have accompanied this
8*2c48331dSMatt Macy  * source.  A copy of the CDDL is also available via the Internet at
9*2c48331dSMatt Macy  * http://www.illumos.org/license/CDDL.
10*2c48331dSMatt Macy  */
11*2c48331dSMatt Macy /*
12*2c48331dSMatt Macy  * Copyright 2020 Toomas Soome <tsoome@me.com>
13*2c48331dSMatt Macy  */
14*2c48331dSMatt Macy 
15*2c48331dSMatt Macy #include <sys/types.h>
16*2c48331dSMatt Macy #include <string.h>
17*2c48331dSMatt Macy #include <libzfs.h>
18*2c48331dSMatt Macy #include <libzfsbootenv.h>
19*2c48331dSMatt Macy #include <sys/zfs_bootenv.h>
20*2c48331dSMatt Macy #include <sys/vdev_impl.h>
21*2c48331dSMatt Macy 
22*2c48331dSMatt Macy /*
23*2c48331dSMatt Macy  * Store device name to zpool label bootenv area.
24*2c48331dSMatt Macy  * This call will set bootenv version to VB_NVLIST, if bootenv currently
25*2c48331dSMatt Macy  * does contain other version, then old data will be replaced.
26*2c48331dSMatt Macy  */
27*2c48331dSMatt Macy int
28*2c48331dSMatt Macy lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device)
29*2c48331dSMatt Macy {
30*2c48331dSMatt Macy 	libzfs_handle_t *hdl;
31*2c48331dSMatt Macy 	zpool_handle_t *zphdl;
32*2c48331dSMatt Macy 	nvlist_t *nv;
33*2c48331dSMatt Macy 	char *descriptor;
34*2c48331dSMatt Macy 	uint64_t version;
35*2c48331dSMatt Macy 	int rv = -1;
36*2c48331dSMatt Macy 
37*2c48331dSMatt Macy 	if (pool == NULL || *pool == '\0')
38*2c48331dSMatt Macy 		return (rv);
39*2c48331dSMatt Macy 
40*2c48331dSMatt Macy 	if ((hdl = libzfs_init()) == NULL)
41*2c48331dSMatt Macy 		return (rv);
42*2c48331dSMatt Macy 
43*2c48331dSMatt Macy 	zphdl = zpool_open(hdl, pool);
44*2c48331dSMatt Macy 	if (zphdl == NULL) {
45*2c48331dSMatt Macy 		libzfs_fini(hdl);
46*2c48331dSMatt Macy 		return (rv);
47*2c48331dSMatt Macy 	}
48*2c48331dSMatt Macy 
49*2c48331dSMatt Macy 	switch (flag) {
50*2c48331dSMatt Macy 	case lzbe_add:
51*2c48331dSMatt Macy 		rv = zpool_get_bootenv(zphdl, &nv);
52*2c48331dSMatt Macy 		if (rv == 0) {
53*2c48331dSMatt Macy 			/*
54*2c48331dSMatt Macy 			 * We got the nvlist, check for version.
55*2c48331dSMatt Macy 			 * if version is missing or is not VB_NVLIST,
56*2c48331dSMatt Macy 			 * create new list.
57*2c48331dSMatt Macy 			 */
58*2c48331dSMatt Macy 			rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION,
59*2c48331dSMatt Macy 			    &version);
60*2c48331dSMatt Macy 			if (rv == 0 && version == VB_NVLIST)
61*2c48331dSMatt Macy 				break;
62*2c48331dSMatt Macy 
63*2c48331dSMatt Macy 			/* Drop this nvlist */
64*2c48331dSMatt Macy 			fnvlist_free(nv);
65*2c48331dSMatt Macy 		}
66*2c48331dSMatt Macy 		/* FALLTHROUGH */
67*2c48331dSMatt Macy 	case lzbe_replace:
68*2c48331dSMatt Macy 		nv = fnvlist_alloc();
69*2c48331dSMatt Macy 		break;
70*2c48331dSMatt Macy 	default:
71*2c48331dSMatt Macy 		return (rv);
72*2c48331dSMatt Macy 	}
73*2c48331dSMatt Macy 
74*2c48331dSMatt Macy 	/* version is mandatory */
75*2c48331dSMatt Macy 	fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST);
76*2c48331dSMatt Macy 
77*2c48331dSMatt Macy 	/*
78*2c48331dSMatt Macy 	 * If device name is empty, remove boot device configuration.
79*2c48331dSMatt Macy 	 */
80*2c48331dSMatt Macy 	if ((device == NULL || *device == '\0')) {
81*2c48331dSMatt Macy 		if (nvlist_exists(nv, OS_BOOTONCE))
82*2c48331dSMatt Macy 			fnvlist_remove(nv, OS_BOOTONCE);
83*2c48331dSMatt Macy 	} else {
84*2c48331dSMatt Macy 		/*
85*2c48331dSMatt Macy 		 * Use device name directly if it does start with
86*2c48331dSMatt Macy 		 * prefix "zfs:". Otherwise, add prefix and sufix.
87*2c48331dSMatt Macy 		 */
88*2c48331dSMatt Macy 		if (strncmp(device, "zfs:", 4) == 0) {
89*2c48331dSMatt Macy 			fnvlist_add_string(nv, OS_BOOTONCE, device);
90*2c48331dSMatt Macy 		} else {
91*2c48331dSMatt Macy 			descriptor = NULL;
92*2c48331dSMatt Macy 			if (asprintf(&descriptor, "zfs:%s:", device) > 0)
93*2c48331dSMatt Macy 				fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
94*2c48331dSMatt Macy 			else
95*2c48331dSMatt Macy 				rv = ENOMEM;
96*2c48331dSMatt Macy 			free(descriptor);
97*2c48331dSMatt Macy 		}
98*2c48331dSMatt Macy 	}
99*2c48331dSMatt Macy 
100*2c48331dSMatt Macy 	rv = zpool_set_bootenv(zphdl, nv);
101*2c48331dSMatt Macy 	if (rv != 0)
102*2c48331dSMatt Macy 		fprintf(stderr, "%s\n", libzfs_error_description(hdl));
103*2c48331dSMatt Macy 
104*2c48331dSMatt Macy 	fnvlist_free(nv);
105*2c48331dSMatt Macy 	zpool_close(zphdl);
106*2c48331dSMatt Macy 	libzfs_fini(hdl);
107*2c48331dSMatt Macy 	return (rv);
108*2c48331dSMatt Macy }
109*2c48331dSMatt Macy 
110*2c48331dSMatt Macy /*
111*2c48331dSMatt Macy  * Return boot device name from bootenv, if set.
112*2c48331dSMatt Macy  */
113*2c48331dSMatt Macy int
114*2c48331dSMatt Macy lzbe_get_boot_device(const char *pool, char **device)
115*2c48331dSMatt Macy {
116*2c48331dSMatt Macy 	libzfs_handle_t *hdl;
117*2c48331dSMatt Macy 	zpool_handle_t *zphdl;
118*2c48331dSMatt Macy 	nvlist_t *nv;
119*2c48331dSMatt Macy 	char *val;
120*2c48331dSMatt Macy 	int rv = -1;
121*2c48331dSMatt Macy 
122*2c48331dSMatt Macy 	if (pool == NULL || *pool == '\0' || device == NULL)
123*2c48331dSMatt Macy 		return (rv);
124*2c48331dSMatt Macy 
125*2c48331dSMatt Macy 	if ((hdl = libzfs_init()) == NULL)
126*2c48331dSMatt Macy 		return (rv);
127*2c48331dSMatt Macy 
128*2c48331dSMatt Macy 	zphdl = zpool_open(hdl, pool);
129*2c48331dSMatt Macy 	if (zphdl == NULL) {
130*2c48331dSMatt Macy 		libzfs_fini(hdl);
131*2c48331dSMatt Macy 		return (rv);
132*2c48331dSMatt Macy 	}
133*2c48331dSMatt Macy 
134*2c48331dSMatt Macy 	rv = zpool_get_bootenv(zphdl, &nv);
135*2c48331dSMatt Macy 	if (rv == 0) {
136*2c48331dSMatt Macy 		rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
137*2c48331dSMatt Macy 		if (rv == 0) {
138*2c48331dSMatt Macy 			/*
139*2c48331dSMatt Macy 			 * zfs device descriptor is in form of "zfs:dataset:",
140*2c48331dSMatt Macy 			 * we only do need dataset name.
141*2c48331dSMatt Macy 			 */
142*2c48331dSMatt Macy 			if (strncmp(val, "zfs:", 4) == 0) {
143*2c48331dSMatt Macy 				val += 4;
144*2c48331dSMatt Macy 				val = strdup(val);
145*2c48331dSMatt Macy 				if (val != NULL) {
146*2c48331dSMatt Macy 					size_t len = strlen(val);
147*2c48331dSMatt Macy 
148*2c48331dSMatt Macy 					if (val[len - 1] == ':')
149*2c48331dSMatt Macy 						val[len - 1] = '\0';
150*2c48331dSMatt Macy 					*device = val;
151*2c48331dSMatt Macy 				} else {
152*2c48331dSMatt Macy 					rv = ENOMEM;
153*2c48331dSMatt Macy 				}
154*2c48331dSMatt Macy 			} else {
155*2c48331dSMatt Macy 				rv = EINVAL;
156*2c48331dSMatt Macy 			}
157*2c48331dSMatt Macy 		}
158*2c48331dSMatt Macy 		nvlist_free(nv);
159*2c48331dSMatt Macy 	}
160*2c48331dSMatt Macy 
161*2c48331dSMatt Macy 	zpool_close(zphdl);
162*2c48331dSMatt Macy 	libzfs_fini(hdl);
163*2c48331dSMatt Macy 	return (rv);
164*2c48331dSMatt Macy }
165