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