xref: /freebsd/sys/geom/vinum/geom_vinum_drive.c (revision edf8578117e8844e02c0121147f45e4609b30680)
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/cdefs.h>
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/geom_dbg.h>
38 #include <geom/vinum/geom_vinum_var.h>
39 #include <geom/vinum/geom_vinum.h>
40 
41 #define GV_LEGACY_I386	0
42 #define GV_LEGACY_AMD64 1
43 #define GV_LEGACY_SPARC64 2
44 #define GV_LEGACY_POWERPC 3
45 
46 static int	gv_legacy_header_type(uint8_t *, int);
47 
48 /*
49  * Here are the "offset (size)" for the various struct gv_hdr fields,
50  * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
51  * current (cpu & endian agnostic) versions of the on-disk format of the vinum
52  * header structure:
53  *
54  *       i386    amd64   current   field
55  *     -------- -------- --------  -----
56  *       0 ( 8)   0 ( 8)   0 ( 8)  magic
57  *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
58  *      12 (32)  16 (32)  16 (32)  label.sysname
59  *      44 (32)  48 (32)  48 (32)  label.name
60  *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
61  *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
62  *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
63  *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
64  *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
65  *     ======== ======== ========
66  *     100      120      120       total size
67  *
68  * NOTE: i386 and amd64 formats are stored as little-endian; the current
69  * format uses big-endian (network order).
70  */
71 
72 /* Checks for legacy format depending on platform. */
73 static int
74 gv_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  */
111 int
112 gv_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. */
227 int
228 gv_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. */
261 void
262 gv_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