1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2004, 2005, 2007 Lukas Ertl
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/types.h>
30 #include <sys/endian.h>
31 #include <sys/malloc.h>
32 #include <sys/sbuf.h>
33 #include <sys/systm.h>
34
35 #include <geom/geom.h>
36 #include <geom/geom_dbg.h>
37 #include <geom/vinum/geom_vinum_var.h>
38 #include <geom/vinum/geom_vinum.h>
39
40 #define GV_LEGACY_I386 0
41 #define GV_LEGACY_AMD64 1
42 #define GV_LEGACY_SPARC64 2
43 #define GV_LEGACY_POWERPC 3
44
45 static int gv_legacy_header_type(uint8_t *, int);
46
47 /*
48 * Here are the "offset (size)" for the various struct gv_hdr fields,
49 * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
50 * current (cpu & endian agnostic) versions of the on-disk format of the vinum
51 * header structure:
52 *
53 * i386 amd64 current field
54 * -------- -------- -------- -----
55 * 0 ( 8) 0 ( 8) 0 ( 8) magic
56 * 8 ( 4) 8 ( 8) 8 ( 8) config_length
57 * 12 (32) 16 (32) 16 (32) label.sysname
58 * 44 (32) 48 (32) 48 (32) label.name
59 * 76 ( 4) 80 ( 8) 80 ( 8) label.date_of_birth.tv_sec
60 * 80 ( 4) 88 ( 8) 88 ( 8) label.date_of_birth.tv_usec
61 * 84 ( 4) 96 ( 8) 96 ( 8) label.last_update.tv_sec
62 * 88 ( 4) 104 ( 8) 104 ( 8) label.last_update.tv_usec
63 * 92 ( 8) 112 ( 8) 112 ( 8) label.drive_size
64 * ======== ======== ========
65 * 100 120 120 total size
66 *
67 * NOTE: i386 and amd64 formats are stored as little-endian; the current
68 * format uses big-endian (network order).
69 */
70
71 /* Checks for legacy format depending on platform. */
72 static int
gv_legacy_header_type(uint8_t * hdr,int bigendian)73 gv_legacy_header_type(uint8_t *hdr, int bigendian)
74 {
75 uint32_t *i32;
76 int arch_32, arch_64, i;
77
78 /* Set arch according to endianness. */
79 if (bigendian) {
80 arch_32 = GV_LEGACY_POWERPC;
81 arch_64 = GV_LEGACY_SPARC64;
82 } else {
83 arch_32 = GV_LEGACY_I386;
84 arch_64 = GV_LEGACY_AMD64;
85 }
86
87 /* if non-empty hostname overlaps 64-bit config_length */
88 i32 = (uint32_t *)(hdr + 12);
89 if (*i32 != 0)
90 return (arch_32);
91 /* check for non-empty hostname */
92 if (hdr[16] != 0)
93 return (arch_64);
94 /* check bytes past 32-bit structure */
95 for (i = 100; i < 120; i++)
96 if (hdr[i] != 0)
97 return (arch_32);
98 /* check for overlapping timestamp */
99 i32 = (uint32_t *)(hdr + 84);
100
101 if (*i32 == 0)
102 return (arch_64);
103 return (arch_32);
104 }
105
106 /*
107 * Read the header while taking magic number into account, and write it to
108 * destination pointer.
109 */
110 int
gv_read_header(struct g_consumer * cp,struct gv_hdr * m_hdr)111 gv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
112 {
113 struct g_provider *pp;
114 uint64_t magic_machdep;
115 uint8_t *d_hdr;
116 int be, off;
117
118 #define GV_GET32(endian) \
119 endian##32toh(*((uint32_t *)&d_hdr[off])); \
120 off += 4
121 #define GV_GET64(endian) \
122 endian##64toh(*((uint64_t *)&d_hdr[off])); \
123 off += 8
124
125 KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
126 KASSERT(cp != NULL, ("gv_read_header: null cp"));
127 pp = cp->provider;
128 KASSERT(pp != NULL, ("gv_read_header: null pp"));
129
130 if ((GV_HDR_OFFSET % pp->sectorsize) != 0 ||
131 (GV_HDR_LEN % pp->sectorsize) != 0)
132 return (ENODEV);
133
134 d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
135 if (d_hdr == NULL)
136 return (-1);
137 off = 0;
138 m_hdr->magic = GV_GET64(be);
139 magic_machdep = *((uint64_t *)&d_hdr[0]);
140 /*
141 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
142 * need to decide if we are running on a big endian machine as well as
143 * checking the magic against the reverse of GV_OLD_MAGIC.
144 */
145 be = (m_hdr->magic == magic_machdep);
146 if (m_hdr->magic == GV_MAGIC) {
147 m_hdr->config_length = GV_GET64(be);
148 off = 16;
149 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
150 off += GV_HOSTNAME_LEN;
151 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
152 off += GV_MAXDRIVENAME;
153 m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
154 m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
155 m_hdr->label.last_update.tv_sec = GV_GET64(be);
156 m_hdr->label.last_update.tv_usec = GV_GET64(be);
157 m_hdr->label.drive_size = GV_GET64(be);
158 } else if (m_hdr->magic != GV_OLD_MAGIC &&
159 m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
160 /* Not a gvinum drive. */
161 g_free(d_hdr);
162 return (-1);
163 } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
164 G_VINUM_DEBUG(1, "detected legacy sparc64 header");
165 m_hdr->magic = GV_MAGIC;
166 /* Legacy sparc64 on-disk header */
167 m_hdr->config_length = GV_GET64(be);
168 bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
169 off += GV_HOSTNAME_LEN;
170 bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
171 off += GV_MAXDRIVENAME;
172 m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
173 m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
174 m_hdr->label.last_update.tv_sec = GV_GET64(be);
175 m_hdr->label.last_update.tv_usec = GV_GET64(be);
176 m_hdr->label.drive_size = GV_GET64(be);
177 } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
178 G_VINUM_DEBUG(1, "detected legacy PowerPC header");
179 m_hdr->magic = GV_MAGIC;
180 /* legacy 32-bit big endian on-disk header */
181 m_hdr->config_length = GV_GET32(be);
182 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
183 off += GV_HOSTNAME_LEN;
184 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
185 off += GV_MAXDRIVENAME;
186 m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
187 m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
188 m_hdr->label.last_update.tv_sec = GV_GET32(be);
189 m_hdr->label.last_update.tv_usec = GV_GET32(be);
190 m_hdr->label.drive_size = GV_GET64(be);
191 } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
192 G_VINUM_DEBUG(1, "detected legacy i386 header");
193 m_hdr->magic = GV_MAGIC;
194 /* legacy i386 on-disk header */
195 m_hdr->config_length = GV_GET32(le);
196 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
197 off += GV_HOSTNAME_LEN;
198 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
199 off += GV_MAXDRIVENAME;
200 m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
201 m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
202 m_hdr->label.last_update.tv_sec = GV_GET32(le);
203 m_hdr->label.last_update.tv_usec = GV_GET32(le);
204 m_hdr->label.drive_size = GV_GET64(le);
205 } else {
206 G_VINUM_DEBUG(1, "detected legacy amd64 header");
207 m_hdr->magic = GV_MAGIC;
208 /* legacy amd64 on-disk header */
209 m_hdr->config_length = GV_GET64(le);
210 bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
211 off += GV_HOSTNAME_LEN;
212 bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
213 off += GV_MAXDRIVENAME;
214 m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
215 m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
216 m_hdr->label.last_update.tv_sec = GV_GET64(le);
217 m_hdr->label.last_update.tv_usec = GV_GET64(le);
218 m_hdr->label.drive_size = GV_GET64(le);
219 }
220
221 g_free(d_hdr);
222 return (0);
223 }
224
225 /* Write out the gvinum header. */
226 int
gv_write_header(struct g_consumer * cp,struct gv_hdr * m_hdr)227 gv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
228 {
229 uint8_t d_hdr[GV_HDR_LEN];
230 int off, ret;
231
232 #define GV_SET64BE(field) \
233 do { \
234 *((uint64_t *)&d_hdr[off]) = htobe64(field); \
235 off += 8; \
236 } while (0)
237
238 KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
239
240 off = 0;
241 memset(d_hdr, 0, GV_HDR_LEN);
242 GV_SET64BE(m_hdr->magic);
243 GV_SET64BE(m_hdr->config_length);
244 off = 16;
245 bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
246 off += GV_HOSTNAME_LEN;
247 bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
248 off += GV_MAXDRIVENAME;
249 GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
250 GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
251 GV_SET64BE(m_hdr->label.last_update.tv_sec);
252 GV_SET64BE(m_hdr->label.last_update.tv_usec);
253 GV_SET64BE(m_hdr->label.drive_size);
254
255 ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
256 return (ret);
257 }
258
259 /* Save the vinum configuration back to each involved disk. */
260 void
gv_save_config(struct gv_softc * sc)261 gv_save_config(struct gv_softc *sc)
262 {
263 struct g_consumer *cp;
264 struct gv_drive *d;
265 struct gv_hdr *vhdr, *hdr;
266 struct sbuf *sb;
267 struct timeval last_update;
268 int error;
269
270 KASSERT(sc != NULL, ("gv_save_config: null sc"));
271
272 vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
273 vhdr->magic = GV_MAGIC;
274 vhdr->config_length = GV_CFG_LEN;
275 microtime(&last_update);
276
277 sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
278 gv_format_config(sc, sb, 1, NULL);
279 sbuf_finish(sb);
280
281 LIST_FOREACH(d, &sc->drives, drive) {
282 /*
283 * We can't save the config on a drive that isn't up, but
284 * drives that were just created aren't officially up yet, so
285 * we check a special flag.
286 */
287 if (d->state != GV_DRIVE_UP)
288 continue;
289
290 cp = d->consumer;
291 if (cp == NULL) {
292 G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
293 d->name);
294 continue;
295 }
296
297 hdr = d->hdr;
298 if (hdr == NULL) {
299 G_VINUM_DEBUG(0, "drive '%s' has no header",
300 d->name);
301 g_free(vhdr);
302 continue;
303 }
304 bcopy(&last_update, &hdr->label.last_update,
305 sizeof(struct timeval));
306 bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
307 g_topology_lock();
308 error = g_access(cp, 0, 1, 0);
309 if (error) {
310 G_VINUM_DEBUG(0, "g_access failed on "
311 "drive %s, errno %d", d->name, error);
312 g_topology_unlock();
313 continue;
314 }
315 g_topology_unlock();
316
317 error = gv_write_header(cp, vhdr);
318 if (error) {
319 G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
320 "errno %d", d->name, error);
321 g_topology_lock();
322 g_access(cp, 0, -1, 0);
323 g_topology_unlock();
324 continue;
325 }
326 /* First config copy. */
327 error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
328 GV_CFG_LEN);
329 if (error) {
330 G_VINUM_DEBUG(0, "writing first config copy failed on "
331 "drive %s, errno %d", d->name, error);
332 g_topology_lock();
333 g_access(cp, 0, -1, 0);
334 g_topology_unlock();
335 continue;
336 }
337 /* Second config copy. */
338 error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
339 sbuf_data(sb), GV_CFG_LEN);
340 if (error)
341 G_VINUM_DEBUG(0, "writing second config copy failed on "
342 "drive %s, errno %d", d->name, error);
343
344 g_topology_lock();
345 g_access(cp, 0, -1, 0);
346 g_topology_unlock();
347 }
348
349 sbuf_delete(sb);
350 g_free(vhdr);
351 }
352