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