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 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 } 347