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