1130389Sle/*- 2190507Slulf * Copyright (c) 2004, 2005, 2007 Lukas Ertl 3130389Sle * All rights reserved. 4130389Sle * 5130389Sle * Redistribution and use in source and binary forms, with or without 6130389Sle * modification, are permitted provided that the following conditions 7130389Sle * are met: 8130389Sle * 1. Redistributions of source code must retain the above copyright 9130389Sle * notice, this list of conditions and the following disclaimer. 10130389Sle * 2. Redistributions in binary form must reproduce the above copyright 11130389Sle * notice, this list of conditions and the following disclaimer in the 12130389Sle * documentation and/or other materials provided with the distribution. 13130389Sle * 14130389Sle * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15130389Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16130389Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17130389Sle * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18130389Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19130389Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20130389Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21130389Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22130389Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23130389Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24130389Sle * SUCH DAMAGE. 25130389Sle */ 26130389Sle 27130389Sle#include <sys/cdefs.h> 28130389Sle__FBSDID("$FreeBSD$"); 29130389Sle 30223921Sae#include <sys/types.h> 31183514Slulf#include <sys/endian.h> 32130389Sle#include <sys/malloc.h> 33223921Sae#include <sys/sbuf.h> 34130389Sle#include <sys/systm.h> 35130389Sle 36130389Sle#include <geom/geom.h> 37130389Sle#include <geom/vinum/geom_vinum_var.h> 38130389Sle#include <geom/vinum/geom_vinum.h> 39130389Sle 40183514Slulf#define GV_LEGACY_I386 0 41183514Slulf#define GV_LEGACY_AMD64 1 42183514Slulf#define GV_LEGACY_SPARC64 2 43183514Slulf#define GV_LEGACY_POWERPC 3 44183514Slulf 45183514Slulfstatic int gv_legacy_header_type(uint8_t *, int); 46130389Sle 47183514Slulf/* 48183514Slulf * Here are the "offset (size)" for the various struct gv_hdr fields, 49183514Slulf * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and 50183514Slulf * current (cpu & endian agnostic) versions of the on-disk format of the vinum 51183514Slulf * header structure: 52183514Slulf * 53183514Slulf * i386 amd64 current field 54183514Slulf * -------- -------- -------- ----- 55183514Slulf * 0 ( 8) 0 ( 8) 0 ( 8) magic 56183514Slulf * 8 ( 4) 8 ( 8) 8 ( 8) config_length 57183514Slulf * 12 (32) 16 (32) 16 (32) label.sysname 58183514Slulf * 44 (32) 48 (32) 48 (32) label.name 59183514Slulf * 76 ( 4) 80 ( 8) 80 ( 8) label.date_of_birth.tv_sec 60183514Slulf * 80 ( 4) 88 ( 8) 88 ( 8) label.date_of_birth.tv_usec 61183514Slulf * 84 ( 4) 96 ( 8) 96 ( 8) label.last_update.tv_sec 62183514Slulf * 88 ( 4) 104 ( 8) 104 ( 8) label.last_update.tv_usec 63183514Slulf * 92 ( 8) 112 ( 8) 112 ( 8) label.drive_size 64183514Slulf * ======== ======== ======== 65183514Slulf * 100 120 120 total size 66183514Slulf * 67183514Slulf * NOTE: i386 and amd64 formats are stored as little-endian; the current 68183514Slulf * format uses big-endian (network order). 69183514Slulf */ 70183514Slulf 71183514Slulf 72183514Slulf/* Checks for legacy format depending on platform. */ 73183514Slulfstatic int 74183514Slulfgv_legacy_header_type(uint8_t *hdr, int bigendian) 75183514Slulf{ 76183514Slulf uint32_t *i32; 77183514Slulf int arch_32, arch_64, i; 78183514Slulf 79183514Slulf /* Set arch according to endianess. */ 80183514Slulf if (bigendian) { 81183514Slulf arch_32 = GV_LEGACY_POWERPC; 82183514Slulf arch_64 = GV_LEGACY_SPARC64; 83183514Slulf } else { 84183514Slulf arch_32 = GV_LEGACY_I386; 85183514Slulf arch_64 = GV_LEGACY_AMD64; 86183514Slulf } 87183514Slulf 88183514Slulf /* if non-empty hostname overlaps 64-bit config_length */ 89183514Slulf i32 = (uint32_t *)(hdr + 12); 90183514Slulf if (*i32 != 0) 91183514Slulf return (arch_32); 92183514Slulf /* check for non-empty hostname */ 93183514Slulf if (hdr[16] != 0) 94183514Slulf return (arch_64); 95183514Slulf /* check bytes past 32-bit structure */ 96183514Slulf for (i = 100; i < 120; i++) 97183514Slulf if (hdr[i] != 0) 98183514Slulf return (arch_32); 99183514Slulf /* check for overlapping timestamp */ 100183514Slulf i32 = (uint32_t *)(hdr + 84); 101183514Slulf 102183514Slulf if (*i32 == 0) 103183514Slulf return (arch_64); 104183514Slulf return (arch_32); 105183514Slulf} 106183514Slulf 107183514Slulf/* 108183514Slulf * Read the header while taking magic number into account, and write it to 109183514Slulf * destination pointer. 110183514Slulf */ 111183514Slulfint 112183514Slulfgv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr) 113183514Slulf{ 114183514Slulf struct g_provider *pp; 115183514Slulf uint64_t magic_machdep; 116183514Slulf uint8_t *d_hdr; 117183514Slulf int be, off; 118183514Slulf 119183514Slulf#define GV_GET32(endian) \ 120183514Slulf endian##32toh(*((uint32_t *)&d_hdr[off])); \ 121183514Slulf off += 4 122183514Slulf#define GV_GET64(endian) \ 123183514Slulf endian##64toh(*((uint64_t *)&d_hdr[off])); \ 124183514Slulf off += 8 125183514Slulf 126183514Slulf KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr")); 127183514Slulf KASSERT(cp != NULL, ("gv_read_header: null cp")); 128183514Slulf pp = cp->provider; 129183514Slulf KASSERT(pp != NULL, ("gv_read_header: null pp")); 130183514Slulf 131222283Sae if ((GV_HDR_OFFSET % pp->sectorsize) != 0 || 132222283Sae (GV_HDR_LEN % pp->sectorsize) != 0) 133222283Sae return (ENODEV); 134222283Sae 135183514Slulf d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL); 136183514Slulf if (d_hdr == NULL) 137183514Slulf return (-1); 138183514Slulf off = 0; 139183514Slulf m_hdr->magic = GV_GET64(be); 140183514Slulf magic_machdep = *((uint64_t *)&d_hdr[0]); 141183514Slulf /* 142183514Slulf * The big endian machines will have a reverse of GV_OLD_MAGIC, so we 143183514Slulf * need to decide if we are running on a big endian machine as well as 144183514Slulf * checking the magic against the reverse of GV_OLD_MAGIC. 145183514Slulf */ 146183514Slulf be = (m_hdr->magic == magic_machdep); 147183514Slulf if (m_hdr->magic == GV_MAGIC) { 148183514Slulf m_hdr->config_length = GV_GET64(be); 149183514Slulf off = 16; 150183514Slulf bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN); 151183514Slulf off += GV_HOSTNAME_LEN; 152183514Slulf bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME); 153183514Slulf off += GV_MAXDRIVENAME; 154183514Slulf m_hdr->label.date_of_birth.tv_sec = GV_GET64(be); 155183514Slulf m_hdr->label.date_of_birth.tv_usec = GV_GET64(be); 156183514Slulf m_hdr->label.last_update.tv_sec = GV_GET64(be); 157183514Slulf m_hdr->label.last_update.tv_usec = GV_GET64(be); 158183514Slulf m_hdr->label.drive_size = GV_GET64(be); 159183514Slulf } else if (m_hdr->magic != GV_OLD_MAGIC && 160183514Slulf m_hdr->magic != le64toh(GV_OLD_MAGIC)) { 161183514Slulf /* Not a gvinum drive. */ 162183514Slulf g_free(d_hdr); 163183514Slulf return (-1); 164183514Slulf } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) { 165184292Slulf G_VINUM_DEBUG(1, "detected legacy sparc64 header"); 166183514Slulf m_hdr->magic = GV_MAGIC; 167183514Slulf /* Legacy sparc64 on-disk header */ 168183514Slulf m_hdr->config_length = GV_GET64(be); 169183514Slulf bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN); 170183514Slulf off += GV_HOSTNAME_LEN; 171183514Slulf bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME); 172183514Slulf off += GV_MAXDRIVENAME; 173183514Slulf m_hdr->label.date_of_birth.tv_sec = GV_GET64(be); 174183514Slulf m_hdr->label.date_of_birth.tv_usec = GV_GET64(be); 175183514Slulf m_hdr->label.last_update.tv_sec = GV_GET64(be); 176183514Slulf m_hdr->label.last_update.tv_usec = GV_GET64(be); 177183514Slulf m_hdr->label.drive_size = GV_GET64(be); 178183514Slulf } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) { 179184292Slulf G_VINUM_DEBUG(1, "detected legacy PowerPC header"); 180183514Slulf m_hdr->magic = GV_MAGIC; 181183514Slulf /* legacy 32-bit big endian on-disk header */ 182183514Slulf m_hdr->config_length = GV_GET32(be); 183183514Slulf bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN); 184183514Slulf off += GV_HOSTNAME_LEN; 185183514Slulf bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME); 186183514Slulf off += GV_MAXDRIVENAME; 187183514Slulf m_hdr->label.date_of_birth.tv_sec = GV_GET32(be); 188183514Slulf m_hdr->label.date_of_birth.tv_usec = GV_GET32(be); 189183514Slulf m_hdr->label.last_update.tv_sec = GV_GET32(be); 190183514Slulf m_hdr->label.last_update.tv_usec = GV_GET32(be); 191183514Slulf m_hdr->label.drive_size = GV_GET64(be); 192183514Slulf } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) { 193184292Slulf G_VINUM_DEBUG(1, "detected legacy i386 header"); 194183514Slulf m_hdr->magic = GV_MAGIC; 195183514Slulf /* legacy i386 on-disk header */ 196183514Slulf m_hdr->config_length = GV_GET32(le); 197183514Slulf bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN); 198183514Slulf off += GV_HOSTNAME_LEN; 199183514Slulf bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME); 200183514Slulf off += GV_MAXDRIVENAME; 201183514Slulf m_hdr->label.date_of_birth.tv_sec = GV_GET32(le); 202183514Slulf m_hdr->label.date_of_birth.tv_usec = GV_GET32(le); 203183514Slulf m_hdr->label.last_update.tv_sec = GV_GET32(le); 204183514Slulf m_hdr->label.last_update.tv_usec = GV_GET32(le); 205183514Slulf m_hdr->label.drive_size = GV_GET64(le); 206183514Slulf } else { 207184292Slulf G_VINUM_DEBUG(1, "detected legacy amd64 header"); 208183514Slulf m_hdr->magic = GV_MAGIC; 209183514Slulf /* legacy amd64 on-disk header */ 210183514Slulf m_hdr->config_length = GV_GET64(le); 211183514Slulf bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN); 212183514Slulf off += GV_HOSTNAME_LEN; 213183514Slulf bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME); 214183514Slulf off += GV_MAXDRIVENAME; 215183514Slulf m_hdr->label.date_of_birth.tv_sec = GV_GET64(le); 216183514Slulf m_hdr->label.date_of_birth.tv_usec = GV_GET64(le); 217183514Slulf m_hdr->label.last_update.tv_sec = GV_GET64(le); 218183514Slulf m_hdr->label.last_update.tv_usec = GV_GET64(le); 219183514Slulf m_hdr->label.drive_size = GV_GET64(le); 220183514Slulf } 221183514Slulf 222183514Slulf g_free(d_hdr); 223183514Slulf return (0); 224183514Slulf} 225183514Slulf 226183514Slulf/* Write out the gvinum header. */ 227183514Slulfint 228183514Slulfgv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr) 229183514Slulf{ 230183514Slulf uint8_t d_hdr[GV_HDR_LEN]; 231183514Slulf int off, ret; 232183514Slulf 233183514Slulf#define GV_SET64BE(field) \ 234183514Slulf do { \ 235183514Slulf *((uint64_t *)&d_hdr[off]) = htobe64(field); \ 236183514Slulf off += 8; \ 237183514Slulf } while (0) 238183514Slulf 239183514Slulf KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr")); 240183514Slulf 241183514Slulf off = 0; 242183514Slulf memset(d_hdr, 0, GV_HDR_LEN); 243183514Slulf GV_SET64BE(m_hdr->magic); 244183514Slulf GV_SET64BE(m_hdr->config_length); 245183514Slulf off = 16; 246183514Slulf bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN); 247183514Slulf off += GV_HOSTNAME_LEN; 248183514Slulf bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME); 249183514Slulf off += GV_MAXDRIVENAME; 250183514Slulf GV_SET64BE(m_hdr->label.date_of_birth.tv_sec); 251183514Slulf GV_SET64BE(m_hdr->label.date_of_birth.tv_usec); 252183514Slulf GV_SET64BE(m_hdr->label.last_update.tv_sec); 253183514Slulf GV_SET64BE(m_hdr->label.last_update.tv_usec); 254183514Slulf GV_SET64BE(m_hdr->label.drive_size); 255183514Slulf 256183514Slulf ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN); 257183514Slulf return (ret); 258183514Slulf} 259183514Slulf 260190507Slulf/* Save the vinum configuration back to each involved disk. */ 261130389Slevoid 262190507Slulfgv_save_config(struct gv_softc *sc) 263134407Sle{ 264190507Slulf struct g_consumer *cp; 265130389Sle struct gv_drive *d; 266130389Sle struct gv_hdr *vhdr, *hdr; 267130389Sle struct sbuf *sb; 268190507Slulf struct timeval last_update; 269130389Sle int error; 270130389Sle 271130389Sle KASSERT(sc != NULL, ("gv_save_config: null sc")); 272130389Sle 273130389Sle vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); 274130389Sle vhdr->magic = GV_MAGIC; 275130389Sle vhdr->config_length = GV_CFG_LEN; 276190507Slulf microtime(&last_update); 277130389Sle 278130389Sle sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 279130389Sle gv_format_config(sc, sb, 1, NULL); 280130389Sle sbuf_finish(sb); 281130389Sle 282190507Slulf LIST_FOREACH(d, &sc->drives, drive) { 283190507Slulf /* 284190507Slulf * We can't save the config on a drive that isn't up, but 285190507Slulf * drives that were just created aren't officially up yet, so 286190507Slulf * we check a special flag. 287190507Slulf */ 288190507Slulf if (d->state != GV_DRIVE_UP) 289130389Sle continue; 290130389Sle 291190507Slulf cp = d->consumer; 292190507Slulf if (cp == NULL) { 293190507Slulf G_VINUM_DEBUG(0, "drive '%s' has no consumer!", 294190507Slulf d->name); 295135173Sle continue; 296130389Sle } 297130389Sle 298190507Slulf hdr = d->hdr; 299190507Slulf if (hdr == NULL) { 300190507Slulf G_VINUM_DEBUG(0, "drive '%s' has no header", 301190507Slulf d->name); 302130389Sle g_free(vhdr); 303190507Slulf continue; 304130389Sle } 305190507Slulf bcopy(&last_update, &hdr->label.last_update, 306190507Slulf sizeof(struct timeval)); 307190507Slulf bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label)); 308149501Sle g_topology_lock(); 309190507Slulf error = g_access(cp, 0, 1, 0); 310190507Slulf if (error) { 311190507Slulf G_VINUM_DEBUG(0, "g_access failed on " 312190507Slulf "drive %s, errno %d", d->name, error); 313190507Slulf g_topology_unlock(); 314190507Slulf continue; 315140475Sle } 316190507Slulf g_topology_unlock(); 317140475Sle 318190507Slulf error = gv_write_header(cp, vhdr); 319190507Slulf if (error) { 320190507Slulf G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, " 321190507Slulf "errno %d", d->name, error); 322190507Slulf g_topology_lock(); 323190507Slulf g_access(cp, 0, -1, 0); 324190507Slulf g_topology_unlock(); 325190507Slulf continue; 326154075Sle } 327190507Slulf /* First config copy. */ 328190507Slulf error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb), 329190507Slulf GV_CFG_LEN); 330190507Slulf if (error) { 331190507Slulf G_VINUM_DEBUG(0, "writing first config copy failed on " 332190507Slulf "drive %s, errno %d", d->name, error); 333190507Slulf g_topology_lock(); 334190507Slulf g_access(cp, 0, -1, 0); 335190507Slulf g_topology_unlock(); 336130389Sle continue; 337146325Sle } 338190507Slulf /* Second config copy. */ 339190507Slulf error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN, 340190507Slulf sbuf_data(sb), GV_CFG_LEN); 341190507Slulf if (error) 342190507Slulf G_VINUM_DEBUG(0, "writing second config copy failed on " 343190507Slulf "drive %s, errno %d", d->name, error); 344146325Sle 345190507Slulf g_topology_lock(); 346190507Slulf g_access(cp, 0, -1, 0); 347190507Slulf g_topology_unlock(); 348146325Sle } 349146325Sle 350190507Slulf sbuf_delete(sb); 351190507Slulf g_free(vhdr); 352130389Sle} 353