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