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 __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 } 355