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