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