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