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