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