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