]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/lib/libzfsbootenv/lzbe_device.c
MFV 2.0-rc2
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / lib / libzfsbootenv / lzbe_device.c
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 sufix.
87                  */
88                 if (strncmp(device, "zfs:", 4) == 0) {
89                         fnvlist_add_string(nv, OS_BOOTONCE, device);
90                 } else {
91                         descriptor = NULL;
92                         if (asprintf(&descriptor, "zfs:%s:", device) > 0)
93                                 fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
94                         else
95                                 rv = ENOMEM;
96                         free(descriptor);
97                 }
98         }
99
100         rv = zpool_set_bootenv(zphdl, nv);
101         if (rv != 0)
102                 fprintf(stderr, "%s\n", libzfs_error_description(hdl));
103
104         fnvlist_free(nv);
105         zpool_close(zphdl);
106         libzfs_fini(hdl);
107         return (rv);
108 }
109
110 /*
111  * Return boot device name from bootenv, if set.
112  */
113 int
114 lzbe_get_boot_device(const char *pool, char **device)
115 {
116         libzfs_handle_t *hdl;
117         zpool_handle_t *zphdl;
118         nvlist_t *nv;
119         char *val;
120         int rv = -1;
121
122         if (pool == NULL || *pool == '\0' || device == NULL)
123                 return (rv);
124
125         if ((hdl = libzfs_init()) == NULL)
126                 return (rv);
127
128         zphdl = zpool_open(hdl, pool);
129         if (zphdl == NULL) {
130                 libzfs_fini(hdl);
131                 return (rv);
132         }
133
134         rv = zpool_get_bootenv(zphdl, &nv);
135         if (rv == 0) {
136                 rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
137                 if (rv == 0) {
138                         /*
139                          * zfs device descriptor is in form of "zfs:dataset:",
140                          * we only do need dataset name.
141                          */
142                         if (strncmp(val, "zfs:", 4) == 0) {
143                                 val += 4;
144                                 val = strdup(val);
145                                 if (val != NULL) {
146                                         size_t len = strlen(val);
147
148                                         if (val[len - 1] == ':')
149                                                 val[len - 1] = '\0';
150                                         *device = val;
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 }