1130389Sle/*-
2190507Slulf * Copyright (c) 2004, 2005, 2007 Lukas Ertl
3130389Sle * All rights reserved.
4130389Sle *
5130389Sle * Redistribution and use in source and binary forms, with or without
6130389Sle * modification, are permitted provided that the following conditions
7130389Sle * are met:
8130389Sle * 1. Redistributions of source code must retain the above copyright
9130389Sle *    notice, this list of conditions and the following disclaimer.
10130389Sle * 2. Redistributions in binary form must reproduce the above copyright
11130389Sle *    notice, this list of conditions and the following disclaimer in the
12130389Sle *    documentation and/or other materials provided with the distribution.
13130389Sle *
14130389Sle * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15130389Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16130389Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17130389Sle * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18130389Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19130389Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20130389Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21130389Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22130389Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23130389Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24130389Sle * SUCH DAMAGE.
25130389Sle */
26130389Sle
27130389Sle#include <sys/cdefs.h>
28130389Sle__FBSDID("$FreeBSD$");
29130389Sle
30223921Sae#include <sys/types.h>
31183514Slulf#include <sys/endian.h>
32130389Sle#include <sys/malloc.h>
33223921Sae#include <sys/sbuf.h>
34130389Sle#include <sys/systm.h>
35130389Sle
36130389Sle#include <geom/geom.h>
37130389Sle#include <geom/vinum/geom_vinum_var.h>
38130389Sle#include <geom/vinum/geom_vinum.h>
39130389Sle
40183514Slulf#define GV_LEGACY_I386	0
41183514Slulf#define GV_LEGACY_AMD64 1
42183514Slulf#define GV_LEGACY_SPARC64 2
43183514Slulf#define GV_LEGACY_POWERPC 3
44183514Slulf
45183514Slulfstatic int	gv_legacy_header_type(uint8_t *, int);
46130389Sle
47183514Slulf/*
48183514Slulf * Here are the "offset (size)" for the various struct gv_hdr fields,
49183514Slulf * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
50183514Slulf * current (cpu & endian agnostic) versions of the on-disk format of the vinum
51183514Slulf * header structure:
52183514Slulf *
53183514Slulf *       i386    amd64   current   field
54183514Slulf *     -------- -------- --------  -----
55183514Slulf *       0 ( 8)   0 ( 8)   0 ( 8)  magic
56183514Slulf *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
57183514Slulf *      12 (32)  16 (32)  16 (32)  label.sysname
58183514Slulf *      44 (32)  48 (32)  48 (32)  label.name
59183514Slulf *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
60183514Slulf *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
61183514Slulf *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
62183514Slulf *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
63183514Slulf *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
64183514Slulf *     ======== ======== ========
65183514Slulf *     100      120      120       total size
66183514Slulf *
67183514Slulf * NOTE: i386 and amd64 formats are stored as little-endian; the current
68183514Slulf * format uses big-endian (network order).
69183514Slulf */
70183514Slulf
71183514Slulf
72183514Slulf/* Checks for legacy format depending on platform. */
73183514Slulfstatic int
74183514Slulfgv_legacy_header_type(uint8_t *hdr, int bigendian)
75183514Slulf{
76183514Slulf	uint32_t *i32;
77183514Slulf	int arch_32, arch_64, i;
78183514Slulf
79183514Slulf	/* Set arch according to endianess. */
80183514Slulf	if (bigendian) {
81183514Slulf		arch_32 = GV_LEGACY_POWERPC;
82183514Slulf		arch_64 = GV_LEGACY_SPARC64;
83183514Slulf	} else {
84183514Slulf		arch_32 = GV_LEGACY_I386;
85183514Slulf		arch_64 = GV_LEGACY_AMD64;
86183514Slulf	}
87183514Slulf
88183514Slulf	/* if non-empty hostname overlaps 64-bit config_length */
89183514Slulf	i32 = (uint32_t *)(hdr + 12);
90183514Slulf	if (*i32 != 0)
91183514Slulf		return (arch_32);
92183514Slulf	/* check for non-empty hostname */
93183514Slulf	if (hdr[16] != 0)
94183514Slulf		return (arch_64);
95183514Slulf	/* check bytes past 32-bit structure */
96183514Slulf	for (i = 100; i < 120; i++)
97183514Slulf		if (hdr[i] != 0)
98183514Slulf			return (arch_32);
99183514Slulf	/* check for overlapping timestamp */
100183514Slulf	i32 = (uint32_t *)(hdr + 84);
101183514Slulf
102183514Slulf	if (*i32 == 0)
103183514Slulf		return (arch_64);
104183514Slulf	return (arch_32);
105183514Slulf}
106183514Slulf
107183514Slulf/*
108183514Slulf * Read the header while taking magic number into account, and write it to
109183514Slulf * destination pointer.
110183514Slulf */
111183514Slulfint
112183514Slulfgv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
113183514Slulf{
114183514Slulf	struct g_provider *pp;
115183514Slulf	uint64_t magic_machdep;
116183514Slulf	uint8_t *d_hdr;
117183514Slulf	int be, off;
118183514Slulf
119183514Slulf#define GV_GET32(endian)					\
120183514Slulf		endian##32toh(*((uint32_t *)&d_hdr[off]));	\
121183514Slulf		off += 4
122183514Slulf#define GV_GET64(endian)					\
123183514Slulf		endian##64toh(*((uint64_t *)&d_hdr[off]));	\
124183514Slulf		off += 8
125183514Slulf
126183514Slulf	KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
127183514Slulf	KASSERT(cp != NULL, ("gv_read_header: null cp"));
128183514Slulf	pp = cp->provider;
129183514Slulf	KASSERT(pp != NULL, ("gv_read_header: null pp"));
130183514Slulf
131222283Sae	if ((GV_HDR_OFFSET % pp->sectorsize) != 0 ||
132222283Sae	    (GV_HDR_LEN % pp->sectorsize) != 0)
133222283Sae		return (ENODEV);
134222283Sae
135183514Slulf	d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
136183514Slulf	if (d_hdr == NULL)
137183514Slulf		return (-1);
138183514Slulf	off = 0;
139183514Slulf	m_hdr->magic = GV_GET64(be);
140183514Slulf	magic_machdep = *((uint64_t *)&d_hdr[0]);
141183514Slulf	/*
142183514Slulf	 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
143183514Slulf	 * need to decide if we are running on a big endian machine as well as
144183514Slulf	 * checking the magic against the reverse of GV_OLD_MAGIC.
145183514Slulf	 */
146183514Slulf	be = (m_hdr->magic == magic_machdep);
147183514Slulf	if (m_hdr->magic == GV_MAGIC) {
148183514Slulf		m_hdr->config_length = GV_GET64(be);
149183514Slulf		off = 16;
150183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
151183514Slulf		off += GV_HOSTNAME_LEN;
152183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
153183514Slulf		off += GV_MAXDRIVENAME;
154183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
155183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
156183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(be);
157183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(be);
158183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
159183514Slulf	} else if (m_hdr->magic != GV_OLD_MAGIC &&
160183514Slulf	    m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
161183514Slulf		/* Not a gvinum drive. */
162183514Slulf		g_free(d_hdr);
163183514Slulf		return (-1);
164183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
165184292Slulf		G_VINUM_DEBUG(1, "detected legacy sparc64 header");
166183514Slulf		m_hdr->magic = GV_MAGIC;
167183514Slulf		/* Legacy sparc64 on-disk header */
168183514Slulf		m_hdr->config_length = GV_GET64(be);
169183514Slulf		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
170183514Slulf		off += GV_HOSTNAME_LEN;
171183514Slulf		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
172183514Slulf		off += GV_MAXDRIVENAME;
173183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
174183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
175183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(be);
176183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(be);
177183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
178183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
179184292Slulf		G_VINUM_DEBUG(1, "detected legacy PowerPC header");
180183514Slulf		m_hdr->magic = GV_MAGIC;
181183514Slulf		/* legacy 32-bit big endian on-disk header */
182183514Slulf		m_hdr->config_length = GV_GET32(be);
183183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
184183514Slulf		off += GV_HOSTNAME_LEN;
185183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
186183514Slulf		off += GV_MAXDRIVENAME;
187183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
188183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
189183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET32(be);
190183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET32(be);
191183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
192183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
193184292Slulf		G_VINUM_DEBUG(1, "detected legacy i386 header");
194183514Slulf		m_hdr->magic = GV_MAGIC;
195183514Slulf		/* legacy i386 on-disk header */
196183514Slulf		m_hdr->config_length = GV_GET32(le);
197183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
198183514Slulf		off += GV_HOSTNAME_LEN;
199183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
200183514Slulf		off += GV_MAXDRIVENAME;
201183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
202183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
203183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET32(le);
204183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET32(le);
205183514Slulf		m_hdr->label.drive_size = GV_GET64(le);
206183514Slulf	} else {
207184292Slulf		G_VINUM_DEBUG(1, "detected legacy amd64 header");
208183514Slulf		m_hdr->magic = GV_MAGIC;
209183514Slulf		/* legacy amd64 on-disk header */
210183514Slulf		m_hdr->config_length = GV_GET64(le);
211183514Slulf		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
212183514Slulf		off += GV_HOSTNAME_LEN;
213183514Slulf		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
214183514Slulf		off += GV_MAXDRIVENAME;
215183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
216183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
217183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(le);
218183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(le);
219183514Slulf		m_hdr->label.drive_size = GV_GET64(le);
220183514Slulf	}
221183514Slulf
222183514Slulf	g_free(d_hdr);
223183514Slulf	return (0);
224183514Slulf}
225183514Slulf
226183514Slulf/* Write out the gvinum header. */
227183514Slulfint
228183514Slulfgv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
229183514Slulf{
230183514Slulf	uint8_t d_hdr[GV_HDR_LEN];
231183514Slulf	int off, ret;
232183514Slulf
233183514Slulf#define GV_SET64BE(field)					\
234183514Slulf	do {							\
235183514Slulf		*((uint64_t *)&d_hdr[off]) = htobe64(field);	\
236183514Slulf		off += 8;					\
237183514Slulf	} while (0)
238183514Slulf
239183514Slulf	KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
240183514Slulf
241183514Slulf	off = 0;
242183514Slulf	memset(d_hdr, 0, GV_HDR_LEN);
243183514Slulf	GV_SET64BE(m_hdr->magic);
244183514Slulf	GV_SET64BE(m_hdr->config_length);
245183514Slulf	off = 16;
246183514Slulf	bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
247183514Slulf	off += GV_HOSTNAME_LEN;
248183514Slulf	bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
249183514Slulf	off += GV_MAXDRIVENAME;
250183514Slulf	GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
251183514Slulf	GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
252183514Slulf	GV_SET64BE(m_hdr->label.last_update.tv_sec);
253183514Slulf	GV_SET64BE(m_hdr->label.last_update.tv_usec);
254183514Slulf	GV_SET64BE(m_hdr->label.drive_size);
255183514Slulf
256183514Slulf	ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
257183514Slulf	return (ret);
258183514Slulf}
259183514Slulf
260190507Slulf/* Save the vinum configuration back to each involved disk. */
261130389Slevoid
262190507Slulfgv_save_config(struct gv_softc *sc)
263134407Sle{
264190507Slulf	struct g_consumer *cp;
265130389Sle	struct gv_drive *d;
266130389Sle	struct gv_hdr *vhdr, *hdr;
267130389Sle	struct sbuf *sb;
268190507Slulf	struct timeval last_update;
269130389Sle	int error;
270130389Sle
271130389Sle	KASSERT(sc != NULL, ("gv_save_config: null sc"));
272130389Sle
273130389Sle	vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
274130389Sle	vhdr->magic = GV_MAGIC;
275130389Sle	vhdr->config_length = GV_CFG_LEN;
276190507Slulf	microtime(&last_update);
277130389Sle
278130389Sle	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
279130389Sle	gv_format_config(sc, sb, 1, NULL);
280130389Sle	sbuf_finish(sb);
281130389Sle
282190507Slulf	LIST_FOREACH(d, &sc->drives, drive) {
283190507Slulf		/*
284190507Slulf		 * We can't save the config on a drive that isn't up, but
285190507Slulf		 * drives that were just created aren't officially up yet, so
286190507Slulf		 * we check a special flag.
287190507Slulf		 */
288190507Slulf		if (d->state != GV_DRIVE_UP)
289130389Sle			continue;
290130389Sle
291190507Slulf		cp = d->consumer;
292190507Slulf		if (cp == NULL) {
293190507Slulf			G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
294190507Slulf			    d->name);
295135173Sle			continue;
296130389Sle		}
297130389Sle
298190507Slulf		hdr = d->hdr;
299190507Slulf		if (hdr == NULL) {
300190507Slulf			G_VINUM_DEBUG(0, "drive '%s' has no header",
301190507Slulf			    d->name);
302130389Sle			g_free(vhdr);
303190507Slulf			continue;
304130389Sle		}
305190507Slulf		bcopy(&last_update, &hdr->label.last_update,
306190507Slulf		    sizeof(struct timeval));
307190507Slulf		bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
308149501Sle		g_topology_lock();
309190507Slulf		error = g_access(cp, 0, 1, 0);
310190507Slulf		if (error) {
311190507Slulf			G_VINUM_DEBUG(0, "g_access failed on "
312190507Slulf			    "drive %s, errno %d", d->name, error);
313190507Slulf			g_topology_unlock();
314190507Slulf			continue;
315140475Sle		}
316190507Slulf		g_topology_unlock();
317140475Sle
318190507Slulf		error = gv_write_header(cp, vhdr);
319190507Slulf		if (error) {
320190507Slulf			G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
321190507Slulf			    "errno %d", d->name, error);
322190507Slulf			g_topology_lock();
323190507Slulf			g_access(cp, 0, -1, 0);
324190507Slulf			g_topology_unlock();
325190507Slulf			continue;
326154075Sle		}
327190507Slulf		/* First config copy. */
328190507Slulf		error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
329190507Slulf		    GV_CFG_LEN);
330190507Slulf		if (error) {
331190507Slulf			G_VINUM_DEBUG(0, "writing first config copy failed on "
332190507Slulf			    "drive %s, errno %d", d->name, error);
333190507Slulf			g_topology_lock();
334190507Slulf			g_access(cp, 0, -1, 0);
335190507Slulf			g_topology_unlock();
336130389Sle			continue;
337146325Sle		}
338190507Slulf		/* Second config copy. */
339190507Slulf		error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
340190507Slulf		    sbuf_data(sb), GV_CFG_LEN);
341190507Slulf		if (error)
342190507Slulf			G_VINUM_DEBUG(0, "writing second config copy failed on "
343190507Slulf			    "drive %s, errno %d", d->name, error);
344146325Sle
345190507Slulf		g_topology_lock();
346190507Slulf		g_access(cp, 0, -1, 0);
347190507Slulf		g_topology_unlock();
348146325Sle	}
349146325Sle
350190507Slulf	sbuf_delete(sb);
351190507Slulf	g_free(vhdr);
352130389Sle}
353