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