11d3aed33SMarcel Moolenaar /*- 21d3aed33SMarcel Moolenaar * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar 31d3aed33SMarcel Moolenaar * All rights reserved. 41d3aed33SMarcel Moolenaar * 51d3aed33SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 61d3aed33SMarcel Moolenaar * modification, are permitted provided that the following conditions 71d3aed33SMarcel Moolenaar * are met: 81d3aed33SMarcel Moolenaar * 91d3aed33SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 101d3aed33SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 111d3aed33SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 121d3aed33SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 131d3aed33SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 141d3aed33SMarcel Moolenaar * 151d3aed33SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 161d3aed33SMarcel Moolenaar * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 171d3aed33SMarcel Moolenaar * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 181d3aed33SMarcel Moolenaar * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 191d3aed33SMarcel Moolenaar * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 201d3aed33SMarcel Moolenaar * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 211d3aed33SMarcel Moolenaar * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 221d3aed33SMarcel Moolenaar * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 231d3aed33SMarcel Moolenaar * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 241d3aed33SMarcel Moolenaar * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 251d3aed33SMarcel Moolenaar */ 261d3aed33SMarcel Moolenaar 271d3aed33SMarcel Moolenaar #include <sys/cdefs.h> 281d3aed33SMarcel Moolenaar __FBSDID("$FreeBSD$"); 291d3aed33SMarcel Moolenaar 301d3aed33SMarcel Moolenaar #include <sys/param.h> 311d3aed33SMarcel Moolenaar #include <sys/bio.h> 321d3aed33SMarcel Moolenaar #include <sys/diskmbr.h> 331d3aed33SMarcel Moolenaar #include <sys/endian.h> 341d3aed33SMarcel Moolenaar #include <sys/gpt.h> 351d3aed33SMarcel Moolenaar #include <sys/kernel.h> 361d3aed33SMarcel Moolenaar #include <sys/kobj.h> 371d3aed33SMarcel Moolenaar #include <sys/limits.h> 381d3aed33SMarcel Moolenaar #include <sys/lock.h> 391d3aed33SMarcel Moolenaar #include <sys/malloc.h> 401d3aed33SMarcel Moolenaar #include <sys/mutex.h> 411d3aed33SMarcel Moolenaar #include <sys/queue.h> 421d3aed33SMarcel Moolenaar #include <sys/sbuf.h> 431d3aed33SMarcel Moolenaar #include <sys/systm.h> 441d3aed33SMarcel Moolenaar #include <sys/uuid.h> 451d3aed33SMarcel Moolenaar #include <geom/geom.h> 461d3aed33SMarcel Moolenaar #include <geom/part/g_part.h> 471d3aed33SMarcel Moolenaar 481d3aed33SMarcel Moolenaar #include "g_part_if.h" 491d3aed33SMarcel Moolenaar 501d3aed33SMarcel Moolenaar CTASSERT(offsetof(struct gpt_hdr, padding) == 92); 511d3aed33SMarcel Moolenaar CTASSERT(sizeof(struct gpt_ent) == 128); 521d3aed33SMarcel Moolenaar 531d3aed33SMarcel Moolenaar #define EQUUID(a,b) (memcmp(a, b, sizeof(struct uuid)) == 0) 541d3aed33SMarcel Moolenaar 554d32fcb4SMarcel Moolenaar #define MBRSIZE 512 564d32fcb4SMarcel Moolenaar 571d3aed33SMarcel Moolenaar enum gpt_elt { 581d3aed33SMarcel Moolenaar GPT_ELT_PRIHDR, 591d3aed33SMarcel Moolenaar GPT_ELT_PRITBL, 601d3aed33SMarcel Moolenaar GPT_ELT_SECHDR, 611d3aed33SMarcel Moolenaar GPT_ELT_SECTBL, 621d3aed33SMarcel Moolenaar GPT_ELT_COUNT 631d3aed33SMarcel Moolenaar }; 641d3aed33SMarcel Moolenaar 651d3aed33SMarcel Moolenaar enum gpt_state { 661d3aed33SMarcel Moolenaar GPT_STATE_UNKNOWN, /* Not determined. */ 671d3aed33SMarcel Moolenaar GPT_STATE_MISSING, /* No signature found. */ 681d3aed33SMarcel Moolenaar GPT_STATE_CORRUPT, /* Checksum mismatch. */ 691d3aed33SMarcel Moolenaar GPT_STATE_INVALID, /* Nonconformant/invalid. */ 701d3aed33SMarcel Moolenaar GPT_STATE_OK /* Perfectly fine. */ 711d3aed33SMarcel Moolenaar }; 721d3aed33SMarcel Moolenaar 731d3aed33SMarcel Moolenaar struct g_part_gpt_table { 741d3aed33SMarcel Moolenaar struct g_part_table base; 754d32fcb4SMarcel Moolenaar u_char mbr[MBRSIZE]; 761c2dee3cSRobert Noland struct gpt_hdr *hdr; 771d3aed33SMarcel Moolenaar quad_t lba[GPT_ELT_COUNT]; 781d3aed33SMarcel Moolenaar enum gpt_state state[GPT_ELT_COUNT]; 791d3aed33SMarcel Moolenaar }; 801d3aed33SMarcel Moolenaar 811d3aed33SMarcel Moolenaar struct g_part_gpt_entry { 821d3aed33SMarcel Moolenaar struct g_part_entry base; 831d3aed33SMarcel Moolenaar struct gpt_ent ent; 841d3aed33SMarcel Moolenaar }; 851d3aed33SMarcel Moolenaar 86d3532631SMarcel Moolenaar static void g_gpt_printf_utf16(struct sbuf *, uint16_t *, size_t); 87d3532631SMarcel Moolenaar static void g_gpt_utf8_to_utf16(const uint8_t *, uint16_t *, size_t); 88d3532631SMarcel Moolenaar 891d3aed33SMarcel Moolenaar static int g_part_gpt_add(struct g_part_table *, struct g_part_entry *, 901d3aed33SMarcel Moolenaar struct g_part_parms *); 914d32fcb4SMarcel Moolenaar static int g_part_gpt_bootcode(struct g_part_table *, struct g_part_parms *); 921d3aed33SMarcel Moolenaar static int g_part_gpt_create(struct g_part_table *, struct g_part_parms *); 931d3aed33SMarcel Moolenaar static int g_part_gpt_destroy(struct g_part_table *, struct g_part_parms *); 9451f53a08SWarner Losh static void g_part_gpt_dumpconf(struct g_part_table *, struct g_part_entry *, 955db67052SMarcel Moolenaar struct sbuf *, const char *); 961d3aed33SMarcel Moolenaar static int g_part_gpt_dumpto(struct g_part_table *, struct g_part_entry *); 971d3aed33SMarcel Moolenaar static int g_part_gpt_modify(struct g_part_table *, struct g_part_entry *, 981d3aed33SMarcel Moolenaar struct g_part_parms *); 9951f53a08SWarner Losh static const char *g_part_gpt_name(struct g_part_table *, struct g_part_entry *, 1001d3aed33SMarcel Moolenaar char *, size_t); 1011d3aed33SMarcel Moolenaar static int g_part_gpt_probe(struct g_part_table *, struct g_consumer *); 1021d3aed33SMarcel Moolenaar static int g_part_gpt_read(struct g_part_table *, struct g_consumer *); 1031d3aed33SMarcel Moolenaar static const char *g_part_gpt_type(struct g_part_table *, struct g_part_entry *, 1041d3aed33SMarcel Moolenaar char *, size_t); 1051d3aed33SMarcel Moolenaar static int g_part_gpt_write(struct g_part_table *, struct g_consumer *); 1061d3aed33SMarcel Moolenaar 1071d3aed33SMarcel Moolenaar static kobj_method_t g_part_gpt_methods[] = { 1081d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_add, g_part_gpt_add), 1094d32fcb4SMarcel Moolenaar KOBJMETHOD(g_part_bootcode, g_part_gpt_bootcode), 1101d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_create, g_part_gpt_create), 1111d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_destroy, g_part_gpt_destroy), 1125db67052SMarcel Moolenaar KOBJMETHOD(g_part_dumpconf, g_part_gpt_dumpconf), 1131d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_dumpto, g_part_gpt_dumpto), 1141d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_modify, g_part_gpt_modify), 1151d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_name, g_part_gpt_name), 1161d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_probe, g_part_gpt_probe), 1171d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_read, g_part_gpt_read), 1181d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_type, g_part_gpt_type), 1191d3aed33SMarcel Moolenaar KOBJMETHOD(g_part_write, g_part_gpt_write), 1201d3aed33SMarcel Moolenaar { 0, 0 } 1211d3aed33SMarcel Moolenaar }; 1221d3aed33SMarcel Moolenaar 1231d3aed33SMarcel Moolenaar static struct g_part_scheme g_part_gpt_scheme = { 1241d3aed33SMarcel Moolenaar "GPT", 1251d3aed33SMarcel Moolenaar g_part_gpt_methods, 1261d3aed33SMarcel Moolenaar sizeof(struct g_part_gpt_table), 1271d3aed33SMarcel Moolenaar .gps_entrysz = sizeof(struct g_part_gpt_entry), 1281d3aed33SMarcel Moolenaar .gps_minent = 128, 1291d3aed33SMarcel Moolenaar .gps_maxent = INT_MAX, 1304d32fcb4SMarcel Moolenaar .gps_bootcodesz = MBRSIZE, 1311d3aed33SMarcel Moolenaar }; 1324ffca444SMarcel Moolenaar G_PART_SCHEME_DECLARE(g_part_gpt); 1331d3aed33SMarcel Moolenaar 13487662ab3SRui Paulo static struct uuid gpt_uuid_apple_hfs = GPT_ENT_TYPE_APPLE_HFS; 1351d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_efi = GPT_ENT_TYPE_EFI; 1361d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; 137f352a0d4SJohn Baldwin static struct uuid gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 1381d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 1391d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 1401d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 141a1fedf91SMarcel Moolenaar static struct uuid gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 1421d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_linux_swap = GPT_ENT_TYPE_LINUX_SWAP; 1431d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_mbr = GPT_ENT_TYPE_MBR; 1441d3aed33SMarcel Moolenaar static struct uuid gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 1451d3aed33SMarcel Moolenaar 1461c2dee3cSRobert Noland static struct gpt_hdr * 1471d3aed33SMarcel Moolenaar gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp, 1481c2dee3cSRobert Noland enum gpt_elt elt) 1491d3aed33SMarcel Moolenaar { 1501c2dee3cSRobert Noland struct gpt_hdr *buf, *hdr; 1511d3aed33SMarcel Moolenaar struct g_provider *pp; 1521d3aed33SMarcel Moolenaar quad_t lba, last; 1531d3aed33SMarcel Moolenaar int error; 1541d3aed33SMarcel Moolenaar uint32_t crc, sz; 1551d3aed33SMarcel Moolenaar 1561d3aed33SMarcel Moolenaar pp = cp->provider; 1571d3aed33SMarcel Moolenaar last = (pp->mediasize / pp->sectorsize) - 1; 1581d3aed33SMarcel Moolenaar table->lba[elt] = (elt == GPT_ELT_PRIHDR) ? 1 : last; 1591d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_MISSING; 1601d3aed33SMarcel Moolenaar buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize, 1611d3aed33SMarcel Moolenaar &error); 1621d3aed33SMarcel Moolenaar if (buf == NULL) 1631c2dee3cSRobert Noland return (NULL); 1641c2dee3cSRobert Noland hdr = NULL; 1651c2dee3cSRobert Noland if (memcmp(buf->hdr_sig, GPT_HDR_SIG, sizeof(buf->hdr_sig)) != 0) 1661c2dee3cSRobert Noland goto fail; 1671d3aed33SMarcel Moolenaar 1681d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_CORRUPT; 1691c2dee3cSRobert Noland sz = le32toh(buf->hdr_size); 1701d3aed33SMarcel Moolenaar if (sz < 92 || sz > pp->sectorsize) 1711c2dee3cSRobert Noland goto fail; 1721c2dee3cSRobert Noland 1731c2dee3cSRobert Noland hdr = g_malloc(sz, M_WAITOK | M_ZERO); 1741c2dee3cSRobert Noland bcopy(buf, hdr, sz); 1751d3aed33SMarcel Moolenaar hdr->hdr_size = sz; 1761c2dee3cSRobert Noland 1771c2dee3cSRobert Noland crc = le32toh(buf->hdr_crc_self); 1781c2dee3cSRobert Noland buf->hdr_crc_self = 0; 1791c2dee3cSRobert Noland if (crc32(buf, sz) != crc) 1801c2dee3cSRobert Noland goto fail; 1811d3aed33SMarcel Moolenaar hdr->hdr_crc_self = crc; 1821d3aed33SMarcel Moolenaar 1831d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_INVALID; 1841c2dee3cSRobert Noland hdr->hdr_revision = le32toh(buf->hdr_revision); 1851d3aed33SMarcel Moolenaar if (hdr->hdr_revision < 0x00010000) 1861c2dee3cSRobert Noland goto fail; 1871c2dee3cSRobert Noland hdr->hdr_lba_self = le64toh(buf->hdr_lba_self); 1881d3aed33SMarcel Moolenaar if (hdr->hdr_lba_self != table->lba[elt]) 1891c2dee3cSRobert Noland goto fail; 1901c2dee3cSRobert Noland hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt); 1911d3aed33SMarcel Moolenaar 1921d3aed33SMarcel Moolenaar /* Check the managed area. */ 1931c2dee3cSRobert Noland hdr->hdr_lba_start = le64toh(buf->hdr_lba_start); 1941d3aed33SMarcel Moolenaar if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last) 1951c2dee3cSRobert Noland goto fail; 1961c2dee3cSRobert Noland hdr->hdr_lba_end = le64toh(buf->hdr_lba_end); 1971d3aed33SMarcel Moolenaar if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last) 1981c2dee3cSRobert Noland goto fail; 1991d3aed33SMarcel Moolenaar 2001d3aed33SMarcel Moolenaar /* Check the table location and size of the table. */ 2011c2dee3cSRobert Noland hdr->hdr_entries = le32toh(buf->hdr_entries); 2021c2dee3cSRobert Noland hdr->hdr_entsz = le32toh(buf->hdr_entsz); 2031d3aed33SMarcel Moolenaar if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 || 2041d3aed33SMarcel Moolenaar (hdr->hdr_entsz & 7) != 0) 2051c2dee3cSRobert Noland goto fail; 2061c2dee3cSRobert Noland hdr->hdr_lba_table = le64toh(buf->hdr_lba_table); 2071d3aed33SMarcel Moolenaar if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last) 2081c2dee3cSRobert Noland goto fail; 2091d3aed33SMarcel Moolenaar if (hdr->hdr_lba_table >= hdr->hdr_lba_start && 2101d3aed33SMarcel Moolenaar hdr->hdr_lba_table <= hdr->hdr_lba_end) 2111c2dee3cSRobert Noland goto fail; 2121d3aed33SMarcel Moolenaar lba = hdr->hdr_lba_table + 2131d3aed33SMarcel Moolenaar (hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) / 2141d3aed33SMarcel Moolenaar pp->sectorsize - 1; 2151d3aed33SMarcel Moolenaar if (lba >= last) 2161c2dee3cSRobert Noland goto fail; 2171d3aed33SMarcel Moolenaar if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end) 2181c2dee3cSRobert Noland goto fail; 2191d3aed33SMarcel Moolenaar 2201d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_OK; 2211c2dee3cSRobert Noland le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid); 2221c2dee3cSRobert Noland hdr->hdr_crc_table = le32toh(buf->hdr_crc_table); 2231c2dee3cSRobert Noland 2241c2dee3cSRobert Noland g_free(buf); 2251c2dee3cSRobert Noland return (hdr); 2261c2dee3cSRobert Noland 2271c2dee3cSRobert Noland fail: 2281c2dee3cSRobert Noland if (hdr != NULL) 2291c2dee3cSRobert Noland g_free(hdr); 2301c2dee3cSRobert Noland g_free(buf); 2311c2dee3cSRobert Noland return (NULL); 2321d3aed33SMarcel Moolenaar } 2331d3aed33SMarcel Moolenaar 2341d3aed33SMarcel Moolenaar static struct gpt_ent * 2351d3aed33SMarcel Moolenaar gpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp, 2361d3aed33SMarcel Moolenaar enum gpt_elt elt, struct gpt_hdr *hdr) 2371d3aed33SMarcel Moolenaar { 2381d3aed33SMarcel Moolenaar struct g_provider *pp; 2391d3aed33SMarcel Moolenaar struct gpt_ent *ent, *tbl; 2401d3aed33SMarcel Moolenaar char *buf, *p; 2411d3aed33SMarcel Moolenaar unsigned int idx, sectors, tblsz; 2421d3aed33SMarcel Moolenaar int error; 2431d3aed33SMarcel Moolenaar 2441c2dee3cSRobert Noland if (hdr == NULL) 2451c2dee3cSRobert Noland return (NULL); 2461c2dee3cSRobert Noland 2471d3aed33SMarcel Moolenaar pp = cp->provider; 2481d3aed33SMarcel Moolenaar table->lba[elt] = hdr->hdr_lba_table; 2491d3aed33SMarcel Moolenaar 2501d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_MISSING; 2511d3aed33SMarcel Moolenaar tblsz = hdr->hdr_entries * hdr->hdr_entsz; 2521d3aed33SMarcel Moolenaar sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize; 2531d3aed33SMarcel Moolenaar buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, 2541d3aed33SMarcel Moolenaar sectors * pp->sectorsize, &error); 2551d3aed33SMarcel Moolenaar if (buf == NULL) 2561d3aed33SMarcel Moolenaar return (NULL); 2571d3aed33SMarcel Moolenaar 2581d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_CORRUPT; 2591d3aed33SMarcel Moolenaar if (crc32(buf, tblsz) != hdr->hdr_crc_table) { 2601d3aed33SMarcel Moolenaar g_free(buf); 2611d3aed33SMarcel Moolenaar return (NULL); 2621d3aed33SMarcel Moolenaar } 2631d3aed33SMarcel Moolenaar 2641d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_OK; 2651d3aed33SMarcel Moolenaar tbl = g_malloc(hdr->hdr_entries * sizeof(struct gpt_ent), 2661d3aed33SMarcel Moolenaar M_WAITOK | M_ZERO); 2671d3aed33SMarcel Moolenaar 2681d3aed33SMarcel Moolenaar for (idx = 0, ent = tbl, p = buf; 2691d3aed33SMarcel Moolenaar idx < hdr->hdr_entries; 2701d3aed33SMarcel Moolenaar idx++, ent++, p += hdr->hdr_entsz) { 2711d3aed33SMarcel Moolenaar le_uuid_dec(p, &ent->ent_type); 2721d3aed33SMarcel Moolenaar le_uuid_dec(p + 16, &ent->ent_uuid); 2731d3aed33SMarcel Moolenaar ent->ent_lba_start = le64dec(p + 32); 2741d3aed33SMarcel Moolenaar ent->ent_lba_end = le64dec(p + 40); 2751d3aed33SMarcel Moolenaar ent->ent_attr = le64dec(p + 48); 276d3532631SMarcel Moolenaar /* Keep UTF-16 in little-endian. */ 277d3532631SMarcel Moolenaar bcopy(p + 56, ent->ent_name, sizeof(ent->ent_name)); 2781d3aed33SMarcel Moolenaar } 2791d3aed33SMarcel Moolenaar 2801d3aed33SMarcel Moolenaar g_free(buf); 2811d3aed33SMarcel Moolenaar return (tbl); 2821d3aed33SMarcel Moolenaar } 2831d3aed33SMarcel Moolenaar 2841d3aed33SMarcel Moolenaar static int 2851d3aed33SMarcel Moolenaar gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec) 2861d3aed33SMarcel Moolenaar { 2871d3aed33SMarcel Moolenaar 2881c2dee3cSRobert Noland if (pri == NULL || sec == NULL) 2891c2dee3cSRobert Noland return (0); 2901c2dee3cSRobert Noland 2911d3aed33SMarcel Moolenaar if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid)) 2921d3aed33SMarcel Moolenaar return (0); 2931d3aed33SMarcel Moolenaar return ((pri->hdr_revision == sec->hdr_revision && 2941d3aed33SMarcel Moolenaar pri->hdr_size == sec->hdr_size && 2951d3aed33SMarcel Moolenaar pri->hdr_lba_start == sec->hdr_lba_start && 2961d3aed33SMarcel Moolenaar pri->hdr_lba_end == sec->hdr_lba_end && 2971d3aed33SMarcel Moolenaar pri->hdr_entries == sec->hdr_entries && 2981d3aed33SMarcel Moolenaar pri->hdr_entsz == sec->hdr_entsz && 2991d3aed33SMarcel Moolenaar pri->hdr_crc_table == sec->hdr_crc_table) ? 1 : 0); 3001d3aed33SMarcel Moolenaar } 3011d3aed33SMarcel Moolenaar 3021d3aed33SMarcel Moolenaar static int 3031d3aed33SMarcel Moolenaar gpt_parse_type(const char *type, struct uuid *uuid) 3041d3aed33SMarcel Moolenaar { 3051d3aed33SMarcel Moolenaar struct uuid tmp; 306d287f590SMarcel Moolenaar const char *alias; 3071d3aed33SMarcel Moolenaar int error; 3081d3aed33SMarcel Moolenaar 309d287f590SMarcel Moolenaar if (type[0] == '!') { 310d287f590SMarcel Moolenaar error = parse_uuid(type + 1, &tmp); 3111d3aed33SMarcel Moolenaar if (error) 3121d3aed33SMarcel Moolenaar return (error); 3131d3aed33SMarcel Moolenaar if (EQUUID(&tmp, &gpt_uuid_unused)) 3141d3aed33SMarcel Moolenaar return (EINVAL); 3151d3aed33SMarcel Moolenaar *uuid = tmp; 3161d3aed33SMarcel Moolenaar return (0); 3171d3aed33SMarcel Moolenaar } 318d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_EFI); 319d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 3201d3aed33SMarcel Moolenaar *uuid = gpt_uuid_efi; 3211d3aed33SMarcel Moolenaar return (0); 3221d3aed33SMarcel Moolenaar } 323d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); 324d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 325d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd; 326d287f590SMarcel Moolenaar return (0); 327d287f590SMarcel Moolenaar } 328f352a0d4SJohn Baldwin alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT); 329f352a0d4SJohn Baldwin if (!strcasecmp(type, alias)) { 330f352a0d4SJohn Baldwin *uuid = gpt_uuid_freebsd_boot; 331f352a0d4SJohn Baldwin return (0); 332f352a0d4SJohn Baldwin } 333d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP); 334d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 335d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd_swap; 336d287f590SMarcel Moolenaar return (0); 337d287f590SMarcel Moolenaar } 338d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS); 339d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 340d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd_ufs; 341d287f590SMarcel Moolenaar return (0); 342d287f590SMarcel Moolenaar } 343d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM); 344d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 345d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd_vinum; 346d287f590SMarcel Moolenaar return (0); 347d287f590SMarcel Moolenaar } 348a1fedf91SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS); 349a1fedf91SMarcel Moolenaar if (!strcasecmp(type, alias)) { 350a1fedf91SMarcel Moolenaar *uuid = gpt_uuid_freebsd_zfs; 351a1fedf91SMarcel Moolenaar return (0); 352a1fedf91SMarcel Moolenaar } 353d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_MBR); 354d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 355d287f590SMarcel Moolenaar *uuid = gpt_uuid_mbr; 356d287f590SMarcel Moolenaar return (0); 357d287f590SMarcel Moolenaar } 358d7255ff4SRui Paulo alias = g_part_alias_name(G_PART_ALIAS_APPLE_HFS); 359d7255ff4SRui Paulo if (!strcasecmp(type, alias)) { 360d7255ff4SRui Paulo *uuid = gpt_uuid_apple_hfs; 361d7255ff4SRui Paulo return (0); 362d7255ff4SRui Paulo } 363d287f590SMarcel Moolenaar return (EINVAL); 364d287f590SMarcel Moolenaar } 3651d3aed33SMarcel Moolenaar 3661d3aed33SMarcel Moolenaar static int 3671d3aed33SMarcel Moolenaar g_part_gpt_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 3681d3aed33SMarcel Moolenaar struct g_part_parms *gpp) 3691d3aed33SMarcel Moolenaar { 3701d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 3711d3aed33SMarcel Moolenaar int error; 3721d3aed33SMarcel Moolenaar 3731d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 3741d3aed33SMarcel Moolenaar error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 3751d3aed33SMarcel Moolenaar if (error) 3761d3aed33SMarcel Moolenaar return (error); 3771d3aed33SMarcel Moolenaar kern_uuidgen(&entry->ent.ent_uuid, 1); 3781d3aed33SMarcel Moolenaar entry->ent.ent_lba_start = baseentry->gpe_start; 3791d3aed33SMarcel Moolenaar entry->ent.ent_lba_end = baseentry->gpe_end; 3801d3aed33SMarcel Moolenaar if (baseentry->gpe_deleted) { 3811d3aed33SMarcel Moolenaar entry->ent.ent_attr = 0; 3821d3aed33SMarcel Moolenaar bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 3831d3aed33SMarcel Moolenaar } 384d3532631SMarcel Moolenaar if (gpp->gpp_parms & G_PART_PARM_LABEL) 385d3532631SMarcel Moolenaar g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 386d3532631SMarcel Moolenaar sizeof(entry->ent.ent_name)); 3871d3aed33SMarcel Moolenaar return (0); 3881d3aed33SMarcel Moolenaar } 3891d3aed33SMarcel Moolenaar 3901d3aed33SMarcel Moolenaar static int 3914d32fcb4SMarcel Moolenaar g_part_gpt_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 3924d32fcb4SMarcel Moolenaar { 3934d32fcb4SMarcel Moolenaar struct g_part_gpt_table *table; 39466477112SMarcel Moolenaar size_t codesz; 3954d32fcb4SMarcel Moolenaar 39666477112SMarcel Moolenaar codesz = DOSPARTOFF; 3974d32fcb4SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 39866477112SMarcel Moolenaar bzero(table->mbr, codesz); 39966477112SMarcel Moolenaar codesz = MIN(codesz, gpp->gpp_codesize); 40066477112SMarcel Moolenaar if (codesz > 0) 40166477112SMarcel Moolenaar bcopy(gpp->gpp_codeptr, table->mbr, codesz); 402e80d42ddSRobert Noland 403e80d42ddSRobert Noland /* Mark the PMBR active since some BIOS require it */ 404e80d42ddSRobert Noland table->mbr[DOSPARTOFF] = 0x80; /* status */ 4054d32fcb4SMarcel Moolenaar return (0); 4064d32fcb4SMarcel Moolenaar } 4074d32fcb4SMarcel Moolenaar 4084d32fcb4SMarcel Moolenaar static int 4091d3aed33SMarcel Moolenaar g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp) 4101d3aed33SMarcel Moolenaar { 4111d3aed33SMarcel Moolenaar struct g_provider *pp; 4121d3aed33SMarcel Moolenaar struct g_part_gpt_table *table; 4131d3aed33SMarcel Moolenaar quad_t last; 4141d3aed33SMarcel Moolenaar size_t tblsz; 4151d3aed33SMarcel Moolenaar 4162a1c00ffSMarcel Moolenaar /* We don't nest, which means that our depth should be 0. */ 4172a1c00ffSMarcel Moolenaar if (basetable->gpt_depth != 0) 4182a1c00ffSMarcel Moolenaar return (ENXIO); 4192a1c00ffSMarcel Moolenaar 4201d3aed33SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 4211d3aed33SMarcel Moolenaar pp = gpp->gpp_provider; 4221d3aed33SMarcel Moolenaar tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) + 4231d3aed33SMarcel Moolenaar pp->sectorsize - 1) / pp->sectorsize; 4244d32fcb4SMarcel Moolenaar if (pp->sectorsize < MBRSIZE || 4251d3aed33SMarcel Moolenaar pp->mediasize < (3 + 2 * tblsz + basetable->gpt_entries) * 4261d3aed33SMarcel Moolenaar pp->sectorsize) 4271d3aed33SMarcel Moolenaar return (ENOSPC); 4281d3aed33SMarcel Moolenaar 4291d3aed33SMarcel Moolenaar last = (pp->mediasize / pp->sectorsize) - 1; 4301d3aed33SMarcel Moolenaar 4314d32fcb4SMarcel Moolenaar le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); 43285301372SMarcel Moolenaar table->mbr[DOSPARTOFF + 1] = 0x01; /* shd */ 43385301372SMarcel Moolenaar table->mbr[DOSPARTOFF + 2] = 0x01; /* ssect */ 43485301372SMarcel Moolenaar table->mbr[DOSPARTOFF + 3] = 0x00; /* scyl */ 4354d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 4] = 0xee; /* typ */ 4364d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 5] = 0xff; /* ehd */ 4374d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 6] = 0xff; /* esect */ 4384d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 7] = 0xff; /* ecyl */ 4394d32fcb4SMarcel Moolenaar le32enc(table->mbr + DOSPARTOFF + 8, 1); /* start */ 4404d32fcb4SMarcel Moolenaar le32enc(table->mbr + DOSPARTOFF + 12, MIN(last, 0xffffffffLL)); 4414d32fcb4SMarcel Moolenaar 4421d3aed33SMarcel Moolenaar table->lba[GPT_ELT_PRIHDR] = 1; 4431d3aed33SMarcel Moolenaar table->lba[GPT_ELT_PRITBL] = 2; 4441d3aed33SMarcel Moolenaar table->lba[GPT_ELT_SECHDR] = last; 4451d3aed33SMarcel Moolenaar table->lba[GPT_ELT_SECTBL] = last - tblsz; 4461d3aed33SMarcel Moolenaar 4471c2dee3cSRobert Noland bcopy(GPT_HDR_SIG, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); 4481c2dee3cSRobert Noland table->hdr->hdr_revision = GPT_HDR_REVISION; 4491c2dee3cSRobert Noland table->hdr->hdr_size = offsetof(struct gpt_hdr, padding); 4501c2dee3cSRobert Noland table->hdr->hdr_lba_start = 2 + tblsz; 4511c2dee3cSRobert Noland table->hdr->hdr_lba_end = last - tblsz - 1; 4521c2dee3cSRobert Noland kern_uuidgen(&table->hdr->hdr_uuid, 1); 4531c2dee3cSRobert Noland table->hdr->hdr_entries = basetable->gpt_entries; 4541c2dee3cSRobert Noland table->hdr->hdr_entsz = sizeof(struct gpt_ent); 4551d3aed33SMarcel Moolenaar 4561c2dee3cSRobert Noland basetable->gpt_first = table->hdr->hdr_lba_start; 4571c2dee3cSRobert Noland basetable->gpt_last = table->hdr->hdr_lba_end; 4581d3aed33SMarcel Moolenaar return (0); 4591d3aed33SMarcel Moolenaar } 4601d3aed33SMarcel Moolenaar 4611d3aed33SMarcel Moolenaar static int 4621d3aed33SMarcel Moolenaar g_part_gpt_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 4631d3aed33SMarcel Moolenaar { 4641d3aed33SMarcel Moolenaar 4651d3aed33SMarcel Moolenaar /* 4661d3aed33SMarcel Moolenaar * Wipe the first 2 sectors as well as the last to clear the 4671d3aed33SMarcel Moolenaar * partitioning. 4681d3aed33SMarcel Moolenaar */ 4691d3aed33SMarcel Moolenaar basetable->gpt_smhead |= 3; 4701d3aed33SMarcel Moolenaar basetable->gpt_smtail |= 1; 4711d3aed33SMarcel Moolenaar return (0); 4721d3aed33SMarcel Moolenaar } 4731d3aed33SMarcel Moolenaar 47451f53a08SWarner Losh static void 4755db67052SMarcel Moolenaar g_part_gpt_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 4765db67052SMarcel Moolenaar struct sbuf *sb, const char *indent) 4775db67052SMarcel Moolenaar { 4785db67052SMarcel Moolenaar struct g_part_gpt_entry *entry; 4795db67052SMarcel Moolenaar 4805db67052SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 4810c132595SMarcel Moolenaar if (indent == NULL) { 4820c132595SMarcel Moolenaar /* conftxt: libdisk compatibility */ 4835db67052SMarcel Moolenaar sbuf_printf(sb, " xs GPT xt "); 4845db67052SMarcel Moolenaar sbuf_printf_uuid(sb, &entry->ent.ent_type); 4850c132595SMarcel Moolenaar } else if (entry != NULL) { 4860c132595SMarcel Moolenaar /* confxml: partition entry information */ 487d3532631SMarcel Moolenaar sbuf_printf(sb, "%s<label>", indent); 488d3532631SMarcel Moolenaar g_gpt_printf_utf16(sb, entry->ent.ent_name, 489d3532631SMarcel Moolenaar sizeof(entry->ent.ent_name) >> 1); 490d3532631SMarcel Moolenaar sbuf_printf(sb, "</label>\n"); 4910c132595SMarcel Moolenaar sbuf_printf(sb, "%s<rawtype>", indent); 4920c132595SMarcel Moolenaar sbuf_printf_uuid(sb, &entry->ent.ent_type); 4930c132595SMarcel Moolenaar sbuf_printf(sb, "</rawtype>\n"); 4940c132595SMarcel Moolenaar } else { 4950c132595SMarcel Moolenaar /* confxml: scheme information */ 4960c132595SMarcel Moolenaar } 4975db67052SMarcel Moolenaar } 4985db67052SMarcel Moolenaar 4995db67052SMarcel Moolenaar static int 5001d3aed33SMarcel Moolenaar g_part_gpt_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 5011d3aed33SMarcel Moolenaar { 5021d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 5031d3aed33SMarcel Moolenaar 5041d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 5051d3aed33SMarcel Moolenaar return ((EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd_swap) || 5061d3aed33SMarcel Moolenaar EQUUID(&entry->ent.ent_type, &gpt_uuid_linux_swap)) ? 1 : 0); 5071d3aed33SMarcel Moolenaar } 5081d3aed33SMarcel Moolenaar 5091d3aed33SMarcel Moolenaar static int 5101d3aed33SMarcel Moolenaar g_part_gpt_modify(struct g_part_table *basetable, 5111d3aed33SMarcel Moolenaar struct g_part_entry *baseentry, struct g_part_parms *gpp) 5121d3aed33SMarcel Moolenaar { 5131d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 5141d3aed33SMarcel Moolenaar int error; 5151d3aed33SMarcel Moolenaar 5161d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 517d287f590SMarcel Moolenaar if (gpp->gpp_parms & G_PART_PARM_TYPE) { 5181d3aed33SMarcel Moolenaar error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 5191d3aed33SMarcel Moolenaar if (error) 5201d3aed33SMarcel Moolenaar return (error); 521d287f590SMarcel Moolenaar } 522d3532631SMarcel Moolenaar if (gpp->gpp_parms & G_PART_PARM_LABEL) 523d3532631SMarcel Moolenaar g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 524d3532631SMarcel Moolenaar sizeof(entry->ent.ent_name)); 5251d3aed33SMarcel Moolenaar return (0); 5261d3aed33SMarcel Moolenaar } 5271d3aed33SMarcel Moolenaar 52851f53a08SWarner Losh static const char * 5291d3aed33SMarcel Moolenaar g_part_gpt_name(struct g_part_table *table, struct g_part_entry *baseentry, 5301d3aed33SMarcel Moolenaar char *buf, size_t bufsz) 5311d3aed33SMarcel Moolenaar { 5321d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 5331d3aed33SMarcel Moolenaar char c; 5341d3aed33SMarcel Moolenaar 5351d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 5361d3aed33SMarcel Moolenaar c = (EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd)) ? 's' : 'p'; 5371d3aed33SMarcel Moolenaar snprintf(buf, bufsz, "%c%d", c, baseentry->gpe_index); 5381d3aed33SMarcel Moolenaar return (buf); 5391d3aed33SMarcel Moolenaar } 5401d3aed33SMarcel Moolenaar 5411d3aed33SMarcel Moolenaar static int 5421d3aed33SMarcel Moolenaar g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp) 5431d3aed33SMarcel Moolenaar { 5441d3aed33SMarcel Moolenaar struct g_provider *pp; 5451d3aed33SMarcel Moolenaar char *buf; 5461d3aed33SMarcel Moolenaar int error, res; 5471d3aed33SMarcel Moolenaar 5481d3aed33SMarcel Moolenaar /* We don't nest, which means that our depth should be 0. */ 5491d3aed33SMarcel Moolenaar if (table->gpt_depth != 0) 5501d3aed33SMarcel Moolenaar return (ENXIO); 5511d3aed33SMarcel Moolenaar 5521d3aed33SMarcel Moolenaar pp = cp->provider; 5531d3aed33SMarcel Moolenaar 5541d3aed33SMarcel Moolenaar /* 5551d3aed33SMarcel Moolenaar * Sanity-check the provider. Since the first sector on the provider 5561d3aed33SMarcel Moolenaar * must be a PMBR and a PMBR is 512 bytes large, the sector size 5571d3aed33SMarcel Moolenaar * must be at least 512 bytes. Also, since the theoretical minimum 5581d3aed33SMarcel Moolenaar * number of sectors needed by GPT is 6, any medium that has less 5591d3aed33SMarcel Moolenaar * than 6 sectors is never going to be able to hold a GPT. The 5601d3aed33SMarcel Moolenaar * number 6 comes from: 5611d3aed33SMarcel Moolenaar * 1 sector for the PMBR 5621d3aed33SMarcel Moolenaar * 2 sectors for the GPT headers (each 1 sector) 5631d3aed33SMarcel Moolenaar * 2 sectors for the GPT tables (each 1 sector) 5641d3aed33SMarcel Moolenaar * 1 sector for an actual partition 5651d3aed33SMarcel Moolenaar * It's better to catch this pathological case early than behaving 5661d3aed33SMarcel Moolenaar * pathologically later on... 5671d3aed33SMarcel Moolenaar */ 5684d32fcb4SMarcel Moolenaar if (pp->sectorsize < MBRSIZE || pp->mediasize < 6 * pp->sectorsize) 5691d3aed33SMarcel Moolenaar return (ENOSPC); 5701d3aed33SMarcel Moolenaar 5711d3aed33SMarcel Moolenaar /* Check that there's a MBR. */ 5721d3aed33SMarcel Moolenaar buf = g_read_data(cp, 0L, pp->sectorsize, &error); 5731d3aed33SMarcel Moolenaar if (buf == NULL) 5741d3aed33SMarcel Moolenaar return (error); 5751d3aed33SMarcel Moolenaar res = le16dec(buf + DOSMAGICOFFSET); 5761d3aed33SMarcel Moolenaar g_free(buf); 5771d3aed33SMarcel Moolenaar if (res != DOSMAGIC) 5781d3aed33SMarcel Moolenaar return (ENXIO); 5791d3aed33SMarcel Moolenaar 5801d3aed33SMarcel Moolenaar /* Check that there's a primary header. */ 5811d3aed33SMarcel Moolenaar buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); 5821d3aed33SMarcel Moolenaar if (buf == NULL) 5831d3aed33SMarcel Moolenaar return (error); 5841d3aed33SMarcel Moolenaar res = memcmp(buf, GPT_HDR_SIG, 8); 5851d3aed33SMarcel Moolenaar g_free(buf); 5861d3aed33SMarcel Moolenaar if (res == 0) 5871d3aed33SMarcel Moolenaar return (G_PART_PROBE_PRI_HIGH); 5881d3aed33SMarcel Moolenaar 5891d3aed33SMarcel Moolenaar /* No primary? Check that there's a secondary. */ 5901d3aed33SMarcel Moolenaar buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 5911d3aed33SMarcel Moolenaar &error); 5921d3aed33SMarcel Moolenaar if (buf == NULL) 5931d3aed33SMarcel Moolenaar return (error); 5941d3aed33SMarcel Moolenaar res = memcmp(buf, GPT_HDR_SIG, 8); 5951d3aed33SMarcel Moolenaar g_free(buf); 5961d3aed33SMarcel Moolenaar return ((res == 0) ? G_PART_PROBE_PRI_HIGH : ENXIO); 5971d3aed33SMarcel Moolenaar } 5981d3aed33SMarcel Moolenaar 5991d3aed33SMarcel Moolenaar static int 6001d3aed33SMarcel Moolenaar g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp) 6011d3aed33SMarcel Moolenaar { 6021c2dee3cSRobert Noland struct gpt_hdr *prihdr, *sechdr; 6031d3aed33SMarcel Moolenaar struct gpt_ent *tbl, *pritbl, *sectbl; 6041d3aed33SMarcel Moolenaar struct g_provider *pp; 6051d3aed33SMarcel Moolenaar struct g_part_gpt_table *table; 6061d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 6074d32fcb4SMarcel Moolenaar u_char *buf; 6084d32fcb4SMarcel Moolenaar int error, index; 6091d3aed33SMarcel Moolenaar 6101d3aed33SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 6111d3aed33SMarcel Moolenaar pp = cp->provider; 6121d3aed33SMarcel Moolenaar 6134d32fcb4SMarcel Moolenaar /* Read the PMBR */ 6144d32fcb4SMarcel Moolenaar buf = g_read_data(cp, 0, pp->sectorsize, &error); 6154d32fcb4SMarcel Moolenaar if (buf == NULL) 6164d32fcb4SMarcel Moolenaar return (error); 6174d32fcb4SMarcel Moolenaar bcopy(buf, table->mbr, MBRSIZE); 6184d32fcb4SMarcel Moolenaar g_free(buf); 6194d32fcb4SMarcel Moolenaar 6201d3aed33SMarcel Moolenaar /* Read the primary header and table. */ 6211c2dee3cSRobert Noland prihdr = gpt_read_hdr(table, cp, GPT_ELT_PRIHDR); 6221d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) { 6231c2dee3cSRobert Noland pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, prihdr); 6241d3aed33SMarcel Moolenaar } else { 6251d3aed33SMarcel Moolenaar table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 6261d3aed33SMarcel Moolenaar pritbl = NULL; 6271d3aed33SMarcel Moolenaar } 6281d3aed33SMarcel Moolenaar 6291d3aed33SMarcel Moolenaar /* Read the secondary header and table. */ 6301c2dee3cSRobert Noland sechdr = gpt_read_hdr(table, cp, GPT_ELT_SECHDR); 6311d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) { 6321c2dee3cSRobert Noland sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, sechdr); 6331d3aed33SMarcel Moolenaar } else { 6341d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 6351d3aed33SMarcel Moolenaar sectbl = NULL; 6361d3aed33SMarcel Moolenaar } 6371d3aed33SMarcel Moolenaar 6381d3aed33SMarcel Moolenaar /* Fail if we haven't got any good tables at all. */ 6391d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK && 6401d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 6411d3aed33SMarcel Moolenaar printf("GEOM: %s: corrupt or invalid GPT detected.\n", 6421d3aed33SMarcel Moolenaar pp->name); 6431d3aed33SMarcel Moolenaar printf("GEOM: %s: GPT rejected -- may not be recoverable.\n", 6441d3aed33SMarcel Moolenaar pp->name); 6451d3aed33SMarcel Moolenaar return (EINVAL); 6461d3aed33SMarcel Moolenaar } 6471d3aed33SMarcel Moolenaar 6481d3aed33SMarcel Moolenaar /* 6491d3aed33SMarcel Moolenaar * If both headers are good but they disagree with each other, 6501d3aed33SMarcel Moolenaar * then invalidate one. We prefer to keep the primary header, 6511d3aed33SMarcel Moolenaar * unless the primary table is corrupt. 6521d3aed33SMarcel Moolenaar */ 6531d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK && 6541d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECHDR] == GPT_STATE_OK && 6551c2dee3cSRobert Noland !gpt_matched_hdrs(prihdr, sechdr)) { 656dd0db05dSMarcel Moolenaar if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK) { 6571d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID; 658dd0db05dSMarcel Moolenaar table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 6591c2dee3cSRobert Noland g_free(sechdr); 6601c2dee3cSRobert Noland sechdr = NULL; 661dd0db05dSMarcel Moolenaar } else { 6621d3aed33SMarcel Moolenaar table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID; 663dd0db05dSMarcel Moolenaar table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 6641c2dee3cSRobert Noland g_free(prihdr); 6651c2dee3cSRobert Noland prihdr = NULL; 666dd0db05dSMarcel Moolenaar } 6671d3aed33SMarcel Moolenaar } 6681d3aed33SMarcel Moolenaar 669dd0db05dSMarcel Moolenaar if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK) { 6701d3aed33SMarcel Moolenaar printf("GEOM: %s: the primary GPT table is corrupt or " 6711d3aed33SMarcel Moolenaar "invalid.\n", pp->name); 6721d3aed33SMarcel Moolenaar printf("GEOM: %s: using the secondary instead -- recovery " 6731d3aed33SMarcel Moolenaar "strongly advised.\n", pp->name); 6741d3aed33SMarcel Moolenaar table->hdr = sechdr; 6751c2dee3cSRobert Noland if (prihdr != NULL) 6761c2dee3cSRobert Noland g_free(prihdr); 6771d3aed33SMarcel Moolenaar tbl = sectbl; 6781d3aed33SMarcel Moolenaar if (pritbl != NULL) 6791d3aed33SMarcel Moolenaar g_free(pritbl); 6801d3aed33SMarcel Moolenaar } else { 681dd0db05dSMarcel Moolenaar if (table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 6821d3aed33SMarcel Moolenaar printf("GEOM: %s: the secondary GPT table is corrupt " 6831d3aed33SMarcel Moolenaar "or invalid.\n", pp->name); 6841d3aed33SMarcel Moolenaar printf("GEOM: %s: using the primary only -- recovery " 6851d3aed33SMarcel Moolenaar "suggested.\n", pp->name); 6861d3aed33SMarcel Moolenaar } 6871d3aed33SMarcel Moolenaar table->hdr = prihdr; 6881c2dee3cSRobert Noland if (sechdr != NULL) 6891c2dee3cSRobert Noland g_free(sechdr); 6901d3aed33SMarcel Moolenaar tbl = pritbl; 6911d3aed33SMarcel Moolenaar if (sectbl != NULL) 6921d3aed33SMarcel Moolenaar g_free(sectbl); 6931d3aed33SMarcel Moolenaar } 6941d3aed33SMarcel Moolenaar 6951c2dee3cSRobert Noland basetable->gpt_first = table->hdr->hdr_lba_start; 6961c2dee3cSRobert Noland basetable->gpt_last = table->hdr->hdr_lba_end; 6971c2dee3cSRobert Noland basetable->gpt_entries = table->hdr->hdr_entries; 6981d3aed33SMarcel Moolenaar 6991d3aed33SMarcel Moolenaar for (index = basetable->gpt_entries - 1; index >= 0; index--) { 7001d3aed33SMarcel Moolenaar if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused)) 7011d3aed33SMarcel Moolenaar continue; 7021d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)g_part_new_entry(basetable, 7031d3aed33SMarcel Moolenaar index+1, tbl[index].ent_lba_start, tbl[index].ent_lba_end); 7041d3aed33SMarcel Moolenaar entry->ent = tbl[index]; 7051d3aed33SMarcel Moolenaar } 7061d3aed33SMarcel Moolenaar 7071d3aed33SMarcel Moolenaar g_free(tbl); 7081d3aed33SMarcel Moolenaar return (0); 7091d3aed33SMarcel Moolenaar } 7101d3aed33SMarcel Moolenaar 7111d3aed33SMarcel Moolenaar static const char * 7121d3aed33SMarcel Moolenaar g_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 7131d3aed33SMarcel Moolenaar char *buf, size_t bufsz) 7141d3aed33SMarcel Moolenaar { 7151d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 7161d3aed33SMarcel Moolenaar struct uuid *type; 7171d3aed33SMarcel Moolenaar 7181d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 7191d3aed33SMarcel Moolenaar type = &entry->ent.ent_type; 7201d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_efi)) 7211d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_EFI)); 7221d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd)) 7231d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); 724f352a0d4SJohn Baldwin if (EQUUID(type, &gpt_uuid_freebsd_boot)) 725f352a0d4SJohn Baldwin return (g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT)); 7261d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_swap)) 7271d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP)); 7281d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_ufs)) 7291d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS)); 7301d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_vinum)) 7311d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM)); 732a1fedf91SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_zfs)) 733a1fedf91SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS)); 7341d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_mbr)) 7351d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_MBR)); 736cf231470SMarcel Moolenaar buf[0] = '!'; 737cf231470SMarcel Moolenaar snprintf_uuid(buf + 1, bufsz - 1, type); 7381d3aed33SMarcel Moolenaar return (buf); 7391d3aed33SMarcel Moolenaar } 7401d3aed33SMarcel Moolenaar 7411d3aed33SMarcel Moolenaar static int 7421d3aed33SMarcel Moolenaar g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp) 7431d3aed33SMarcel Moolenaar { 7441d3aed33SMarcel Moolenaar unsigned char *buf, *bp; 7451d3aed33SMarcel Moolenaar struct g_provider *pp; 7461d3aed33SMarcel Moolenaar struct g_part_entry *baseentry; 7471d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 7481d3aed33SMarcel Moolenaar struct g_part_gpt_table *table; 7491d3aed33SMarcel Moolenaar size_t tlbsz; 7501d3aed33SMarcel Moolenaar uint32_t crc; 7511d3aed33SMarcel Moolenaar int error, index; 7521d3aed33SMarcel Moolenaar 7531d3aed33SMarcel Moolenaar pp = cp->provider; 7541d3aed33SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 7551c2dee3cSRobert Noland tlbsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz + 7561d3aed33SMarcel Moolenaar pp->sectorsize - 1) / pp->sectorsize; 7571d3aed33SMarcel Moolenaar 7584d32fcb4SMarcel Moolenaar /* Write the PMBR */ 7591d3aed33SMarcel Moolenaar buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 7604d32fcb4SMarcel Moolenaar bcopy(table->mbr, buf, MBRSIZE); 7611d3aed33SMarcel Moolenaar error = g_write_data(cp, 0, buf, pp->sectorsize); 7621d3aed33SMarcel Moolenaar g_free(buf); 7631d3aed33SMarcel Moolenaar if (error) 7641d3aed33SMarcel Moolenaar return (error); 7651d3aed33SMarcel Moolenaar 7661d3aed33SMarcel Moolenaar /* Allocate space for the header and entries. */ 7671d3aed33SMarcel Moolenaar buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO); 7681d3aed33SMarcel Moolenaar 7691c2dee3cSRobert Noland memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); 7701c2dee3cSRobert Noland le32enc(buf + 8, table->hdr->hdr_revision); 7711c2dee3cSRobert Noland le32enc(buf + 12, table->hdr->hdr_size); 7721c2dee3cSRobert Noland le64enc(buf + 40, table->hdr->hdr_lba_start); 7731c2dee3cSRobert Noland le64enc(buf + 48, table->hdr->hdr_lba_end); 7741c2dee3cSRobert Noland le_uuid_enc(buf + 56, &table->hdr->hdr_uuid); 7751c2dee3cSRobert Noland le32enc(buf + 80, table->hdr->hdr_entries); 7761c2dee3cSRobert Noland le32enc(buf + 84, table->hdr->hdr_entsz); 7771d3aed33SMarcel Moolenaar 7781d3aed33SMarcel Moolenaar LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { 779d287f590SMarcel Moolenaar if (baseentry->gpe_deleted) 780d287f590SMarcel Moolenaar continue; 7811d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 7821d3aed33SMarcel Moolenaar index = baseentry->gpe_index - 1; 7831c2dee3cSRobert Noland bp = buf + pp->sectorsize + table->hdr->hdr_entsz * index; 7841d3aed33SMarcel Moolenaar le_uuid_enc(bp, &entry->ent.ent_type); 7851d3aed33SMarcel Moolenaar le_uuid_enc(bp + 16, &entry->ent.ent_uuid); 7861d3aed33SMarcel Moolenaar le64enc(bp + 32, entry->ent.ent_lba_start); 7871d3aed33SMarcel Moolenaar le64enc(bp + 40, entry->ent.ent_lba_end); 7881d3aed33SMarcel Moolenaar le64enc(bp + 48, entry->ent.ent_attr); 7891d3aed33SMarcel Moolenaar memcpy(bp + 56, entry->ent.ent_name, 7901d3aed33SMarcel Moolenaar sizeof(entry->ent.ent_name)); 7911d3aed33SMarcel Moolenaar } 7921d3aed33SMarcel Moolenaar 7931d3aed33SMarcel Moolenaar crc = crc32(buf + pp->sectorsize, 7941c2dee3cSRobert Noland table->hdr->hdr_entries * table->hdr->hdr_entsz); 7951d3aed33SMarcel Moolenaar le32enc(buf + 88, crc); 7961d3aed33SMarcel Moolenaar 7971d3aed33SMarcel Moolenaar /* Write primary meta-data. */ 7981d3aed33SMarcel Moolenaar le32enc(buf + 16, 0); /* hdr_crc_self. */ 7991d3aed33SMarcel Moolenaar le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_self. */ 8001d3aed33SMarcel Moolenaar le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_alt. */ 8011d3aed33SMarcel Moolenaar le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]); /* hdr_lba_table. */ 8021c2dee3cSRobert Noland crc = crc32(buf, table->hdr->hdr_size); 8031d3aed33SMarcel Moolenaar le32enc(buf + 16, crc); 8041d3aed33SMarcel Moolenaar 8051d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize, 8061d3aed33SMarcel Moolenaar buf + pp->sectorsize, tlbsz * pp->sectorsize); 8071d3aed33SMarcel Moolenaar if (error) 8081d3aed33SMarcel Moolenaar goto out; 8091d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize, 8101d3aed33SMarcel Moolenaar buf, pp->sectorsize); 8111d3aed33SMarcel Moolenaar if (error) 8121d3aed33SMarcel Moolenaar goto out; 8131d3aed33SMarcel Moolenaar 8141d3aed33SMarcel Moolenaar /* Write secondary meta-data. */ 8151d3aed33SMarcel Moolenaar le32enc(buf + 16, 0); /* hdr_crc_self. */ 8161d3aed33SMarcel Moolenaar le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_self. */ 8171d3aed33SMarcel Moolenaar le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_alt. */ 8181d3aed33SMarcel Moolenaar le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]); /* hdr_lba_table. */ 8191c2dee3cSRobert Noland crc = crc32(buf, table->hdr->hdr_size); 8201d3aed33SMarcel Moolenaar le32enc(buf + 16, crc); 8211d3aed33SMarcel Moolenaar 8221d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize, 8231d3aed33SMarcel Moolenaar buf + pp->sectorsize, tlbsz * pp->sectorsize); 8241d3aed33SMarcel Moolenaar if (error) 8251d3aed33SMarcel Moolenaar goto out; 8261d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize, 8271d3aed33SMarcel Moolenaar buf, pp->sectorsize); 8281d3aed33SMarcel Moolenaar 8291d3aed33SMarcel Moolenaar out: 8301d3aed33SMarcel Moolenaar g_free(buf); 8311d3aed33SMarcel Moolenaar return (error); 8321d3aed33SMarcel Moolenaar } 8331d3aed33SMarcel Moolenaar 8341d3aed33SMarcel Moolenaar static void 835d3532631SMarcel Moolenaar g_gpt_printf_utf16(struct sbuf *sb, uint16_t *str, size_t len) 8361d3aed33SMarcel Moolenaar { 8371d3aed33SMarcel Moolenaar u_int bo; 8381d3aed33SMarcel Moolenaar uint32_t ch; 8391d3aed33SMarcel Moolenaar uint16_t c; 8401d3aed33SMarcel Moolenaar 841d3532631SMarcel Moolenaar bo = LITTLE_ENDIAN; /* GPT is little-endian */ 8421d3aed33SMarcel Moolenaar while (len > 0 && *str != 0) { 8431d3aed33SMarcel Moolenaar ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str); 8441d3aed33SMarcel Moolenaar str++, len--; 8451d3aed33SMarcel Moolenaar if ((ch & 0xf800) == 0xd800) { 8461d3aed33SMarcel Moolenaar if (len > 0) { 8471d3aed33SMarcel Moolenaar c = (bo == BIG_ENDIAN) ? be16toh(*str) 8481d3aed33SMarcel Moolenaar : le16toh(*str); 8491d3aed33SMarcel Moolenaar str++, len--; 8501d3aed33SMarcel Moolenaar } else 8511d3aed33SMarcel Moolenaar c = 0xfffd; 8521d3aed33SMarcel Moolenaar if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) { 8531d3aed33SMarcel Moolenaar ch = ((ch & 0x3ff) << 10) + (c & 0x3ff); 8541d3aed33SMarcel Moolenaar ch += 0x10000; 8551d3aed33SMarcel Moolenaar } else 8561d3aed33SMarcel Moolenaar ch = 0xfffd; 8571d3aed33SMarcel Moolenaar } else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */ 8581d3aed33SMarcel Moolenaar bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN; 8591d3aed33SMarcel Moolenaar continue; 8601d3aed33SMarcel Moolenaar } else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */ 8611d3aed33SMarcel Moolenaar continue; 8621d3aed33SMarcel Moolenaar 863d3532631SMarcel Moolenaar /* Write the Unicode character in UTF-8 */ 8641d3aed33SMarcel Moolenaar if (ch < 0x80) 8651d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c", ch); 8661d3aed33SMarcel Moolenaar else if (ch < 0x800) 8671d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6), 8681d3aed33SMarcel Moolenaar 0x80 | (ch & 0x3f)); 8691d3aed33SMarcel Moolenaar else if (ch < 0x10000) 8701d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12), 8711d3aed33SMarcel Moolenaar 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 8721d3aed33SMarcel Moolenaar else if (ch < 0x200000) 8731d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18), 8741d3aed33SMarcel Moolenaar 0x80 | ((ch >> 12) & 0x3f), 8751d3aed33SMarcel Moolenaar 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 8761d3aed33SMarcel Moolenaar } 8771d3aed33SMarcel Moolenaar } 878d3532631SMarcel Moolenaar 879d3532631SMarcel Moolenaar static void 880d3532631SMarcel Moolenaar g_gpt_utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) 881d3532631SMarcel Moolenaar { 882d3532631SMarcel Moolenaar size_t s16idx, s8idx; 883d3532631SMarcel Moolenaar uint32_t utfchar; 884d3532631SMarcel Moolenaar unsigned int c, utfbytes; 885d3532631SMarcel Moolenaar 886d3532631SMarcel Moolenaar s8idx = s16idx = 0; 887d3532631SMarcel Moolenaar utfchar = 0; 888d3532631SMarcel Moolenaar utfbytes = 0; 889d3532631SMarcel Moolenaar bzero(s16, s16len << 1); 890d3532631SMarcel Moolenaar while (s8[s8idx] != 0 && s16idx < s16len) { 891d3532631SMarcel Moolenaar c = s8[s8idx++]; 892d3532631SMarcel Moolenaar if ((c & 0xc0) != 0x80) { 893d3532631SMarcel Moolenaar /* Initial characters. */ 894d3532631SMarcel Moolenaar if (utfbytes != 0) { 895d3532631SMarcel Moolenaar /* Incomplete encoding of previous char. */ 896d3532631SMarcel Moolenaar s16[s16idx++] = htole16(0xfffd); 897d3532631SMarcel Moolenaar } 898d3532631SMarcel Moolenaar if ((c & 0xf8) == 0xf0) { 899d3532631SMarcel Moolenaar utfchar = c & 0x07; 900d3532631SMarcel Moolenaar utfbytes = 3; 901d3532631SMarcel Moolenaar } else if ((c & 0xf0) == 0xe0) { 902d3532631SMarcel Moolenaar utfchar = c & 0x0f; 903d3532631SMarcel Moolenaar utfbytes = 2; 904d3532631SMarcel Moolenaar } else if ((c & 0xe0) == 0xc0) { 905d3532631SMarcel Moolenaar utfchar = c & 0x1f; 906d3532631SMarcel Moolenaar utfbytes = 1; 907d3532631SMarcel Moolenaar } else { 908d3532631SMarcel Moolenaar utfchar = c & 0x7f; 909d3532631SMarcel Moolenaar utfbytes = 0; 910d3532631SMarcel Moolenaar } 911d3532631SMarcel Moolenaar } else { 912d3532631SMarcel Moolenaar /* Followup characters. */ 913d3532631SMarcel Moolenaar if (utfbytes > 0) { 914d3532631SMarcel Moolenaar utfchar = (utfchar << 6) + (c & 0x3f); 915d3532631SMarcel Moolenaar utfbytes--; 916d3532631SMarcel Moolenaar } else if (utfbytes == 0) 917d3532631SMarcel Moolenaar utfbytes = ~0; 918d3532631SMarcel Moolenaar } 919d3532631SMarcel Moolenaar /* 920d3532631SMarcel Moolenaar * Write the complete Unicode character as UTF-16 when we 921d3532631SMarcel Moolenaar * have all the UTF-8 charactars collected. 922d3532631SMarcel Moolenaar */ 923d3532631SMarcel Moolenaar if (utfbytes == 0) { 924d3532631SMarcel Moolenaar /* 925d3532631SMarcel Moolenaar * If we need to write 2 UTF-16 characters, but 926d3532631SMarcel Moolenaar * we only have room for 1, then we truncate the 927d3532631SMarcel Moolenaar * string by writing a 0 instead. 928d3532631SMarcel Moolenaar */ 929d3532631SMarcel Moolenaar if (utfchar >= 0x10000 && s16idx < s16len - 1) { 930d3532631SMarcel Moolenaar s16[s16idx++] = 931d3532631SMarcel Moolenaar htole16(0xd800 | ((utfchar >> 10) - 0x40)); 932d3532631SMarcel Moolenaar s16[s16idx++] = 933d3532631SMarcel Moolenaar htole16(0xdc00 | (utfchar & 0x3ff)); 934d3532631SMarcel Moolenaar } else 935d3532631SMarcel Moolenaar s16[s16idx++] = (utfchar >= 0x10000) ? 0 : 936d3532631SMarcel Moolenaar htole16(utfchar); 937d3532631SMarcel Moolenaar } 938d3532631SMarcel Moolenaar } 939d3532631SMarcel Moolenaar /* 940d3532631SMarcel Moolenaar * If our input string was truncated, append an invalid encoding 941d3532631SMarcel Moolenaar * character to the output string. 942d3532631SMarcel Moolenaar */ 943d3532631SMarcel Moolenaar if (utfbytes != 0 && s16idx < s16len) 944d3532631SMarcel Moolenaar s16[s16idx++] = htole16(0xfffd); 945d3532631SMarcel Moolenaar } 946