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]; 761d3aed33SMarcel Moolenaar 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 *); 945db67052SMarcel Moolenaar static int 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 *); 991d3aed33SMarcel Moolenaar static 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 1461d3aed33SMarcel Moolenaar static void 1471d3aed33SMarcel Moolenaar gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp, 1481d3aed33SMarcel Moolenaar enum gpt_elt elt, struct gpt_hdr *hdr) 1491d3aed33SMarcel Moolenaar { 1501d3aed33SMarcel Moolenaar struct uuid uuid; 1511d3aed33SMarcel Moolenaar struct g_provider *pp; 1521d3aed33SMarcel Moolenaar char *buf; 1531d3aed33SMarcel Moolenaar quad_t lba, last; 1541d3aed33SMarcel Moolenaar int error; 1551d3aed33SMarcel Moolenaar uint32_t crc, sz; 1561d3aed33SMarcel Moolenaar 1571d3aed33SMarcel Moolenaar pp = cp->provider; 1581d3aed33SMarcel Moolenaar last = (pp->mediasize / pp->sectorsize) - 1; 1591d3aed33SMarcel Moolenaar table->lba[elt] = (elt == GPT_ELT_PRIHDR) ? 1 : last; 1601d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_MISSING; 1611d3aed33SMarcel Moolenaar buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize, 1621d3aed33SMarcel Moolenaar &error); 1631d3aed33SMarcel Moolenaar if (buf == NULL) 1641d3aed33SMarcel Moolenaar return; 1651d3aed33SMarcel Moolenaar bcopy(buf, hdr, sizeof(*hdr)); 1661d3aed33SMarcel Moolenaar if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) 1671d3aed33SMarcel Moolenaar return; 1681d3aed33SMarcel Moolenaar 1691d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_CORRUPT; 1701d3aed33SMarcel Moolenaar sz = le32toh(hdr->hdr_size); 1711d3aed33SMarcel Moolenaar if (sz < 92 || sz > pp->sectorsize) 1721d3aed33SMarcel Moolenaar return; 1731d3aed33SMarcel Moolenaar crc = le32toh(hdr->hdr_crc_self); 1741d3aed33SMarcel Moolenaar hdr->hdr_crc_self = 0; 1751d3aed33SMarcel Moolenaar if (crc32(hdr, sz) != crc) 1761d3aed33SMarcel Moolenaar return; 1771d3aed33SMarcel Moolenaar hdr->hdr_size = sz; 1781d3aed33SMarcel Moolenaar hdr->hdr_crc_self = crc; 1791d3aed33SMarcel Moolenaar 1801d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_INVALID; 1811d3aed33SMarcel Moolenaar hdr->hdr_revision = le32toh(hdr->hdr_revision); 1821d3aed33SMarcel Moolenaar if (hdr->hdr_revision < 0x00010000) 1831d3aed33SMarcel Moolenaar return; 1841d3aed33SMarcel Moolenaar hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); 1851d3aed33SMarcel Moolenaar if (hdr->hdr_lba_self != table->lba[elt]) 1861d3aed33SMarcel Moolenaar return; 1871d3aed33SMarcel Moolenaar hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); 1881d3aed33SMarcel Moolenaar 1891d3aed33SMarcel Moolenaar /* Check the managed area. */ 1901d3aed33SMarcel Moolenaar hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); 1911d3aed33SMarcel Moolenaar if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last) 1921d3aed33SMarcel Moolenaar return; 1931d3aed33SMarcel Moolenaar hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); 1941d3aed33SMarcel Moolenaar if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last) 1951d3aed33SMarcel Moolenaar return; 1961d3aed33SMarcel Moolenaar 1971d3aed33SMarcel Moolenaar /* Check the table location and size of the table. */ 1981d3aed33SMarcel Moolenaar hdr->hdr_entries = le32toh(hdr->hdr_entries); 1991d3aed33SMarcel Moolenaar hdr->hdr_entsz = le32toh(hdr->hdr_entsz); 2001d3aed33SMarcel Moolenaar if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 || 2011d3aed33SMarcel Moolenaar (hdr->hdr_entsz & 7) != 0) 2021d3aed33SMarcel Moolenaar return; 2031d3aed33SMarcel Moolenaar hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); 2041d3aed33SMarcel Moolenaar if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last) 2051d3aed33SMarcel Moolenaar return; 2061d3aed33SMarcel Moolenaar if (hdr->hdr_lba_table >= hdr->hdr_lba_start && 2071d3aed33SMarcel Moolenaar hdr->hdr_lba_table <= hdr->hdr_lba_end) 2081d3aed33SMarcel Moolenaar return; 2091d3aed33SMarcel Moolenaar lba = hdr->hdr_lba_table + 2101d3aed33SMarcel Moolenaar (hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) / 2111d3aed33SMarcel Moolenaar pp->sectorsize - 1; 2121d3aed33SMarcel Moolenaar if (lba >= last) 2131d3aed33SMarcel Moolenaar return; 2141d3aed33SMarcel Moolenaar if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end) 2151d3aed33SMarcel Moolenaar return; 2161d3aed33SMarcel Moolenaar 2171d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_OK; 2181d3aed33SMarcel Moolenaar le_uuid_dec(&hdr->hdr_uuid, &uuid); 2191d3aed33SMarcel Moolenaar hdr->hdr_uuid = uuid; 2201d3aed33SMarcel Moolenaar hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); 2211d3aed33SMarcel Moolenaar } 2221d3aed33SMarcel Moolenaar 2231d3aed33SMarcel Moolenaar static struct gpt_ent * 2241d3aed33SMarcel Moolenaar gpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp, 2251d3aed33SMarcel Moolenaar enum gpt_elt elt, struct gpt_hdr *hdr) 2261d3aed33SMarcel Moolenaar { 2271d3aed33SMarcel Moolenaar struct g_provider *pp; 2281d3aed33SMarcel Moolenaar struct gpt_ent *ent, *tbl; 2291d3aed33SMarcel Moolenaar char *buf, *p; 2301d3aed33SMarcel Moolenaar unsigned int idx, sectors, tblsz; 2311d3aed33SMarcel Moolenaar int error; 2321d3aed33SMarcel Moolenaar 2331d3aed33SMarcel Moolenaar pp = cp->provider; 2341d3aed33SMarcel Moolenaar table->lba[elt] = hdr->hdr_lba_table; 2351d3aed33SMarcel Moolenaar 2361d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_MISSING; 2371d3aed33SMarcel Moolenaar tblsz = hdr->hdr_entries * hdr->hdr_entsz; 2381d3aed33SMarcel Moolenaar sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize; 2391d3aed33SMarcel Moolenaar buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, 2401d3aed33SMarcel Moolenaar sectors * pp->sectorsize, &error); 2411d3aed33SMarcel Moolenaar if (buf == NULL) 2421d3aed33SMarcel Moolenaar return (NULL); 2431d3aed33SMarcel Moolenaar 2441d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_CORRUPT; 2451d3aed33SMarcel Moolenaar if (crc32(buf, tblsz) != hdr->hdr_crc_table) { 2461d3aed33SMarcel Moolenaar g_free(buf); 2471d3aed33SMarcel Moolenaar return (NULL); 2481d3aed33SMarcel Moolenaar } 2491d3aed33SMarcel Moolenaar 2501d3aed33SMarcel Moolenaar table->state[elt] = GPT_STATE_OK; 2511d3aed33SMarcel Moolenaar tbl = g_malloc(hdr->hdr_entries * sizeof(struct gpt_ent), 2521d3aed33SMarcel Moolenaar M_WAITOK | M_ZERO); 2531d3aed33SMarcel Moolenaar 2541d3aed33SMarcel Moolenaar for (idx = 0, ent = tbl, p = buf; 2551d3aed33SMarcel Moolenaar idx < hdr->hdr_entries; 2561d3aed33SMarcel Moolenaar idx++, ent++, p += hdr->hdr_entsz) { 2571d3aed33SMarcel Moolenaar le_uuid_dec(p, &ent->ent_type); 2581d3aed33SMarcel Moolenaar le_uuid_dec(p + 16, &ent->ent_uuid); 2591d3aed33SMarcel Moolenaar ent->ent_lba_start = le64dec(p + 32); 2601d3aed33SMarcel Moolenaar ent->ent_lba_end = le64dec(p + 40); 2611d3aed33SMarcel Moolenaar ent->ent_attr = le64dec(p + 48); 262d3532631SMarcel Moolenaar /* Keep UTF-16 in little-endian. */ 263d3532631SMarcel Moolenaar bcopy(p + 56, ent->ent_name, sizeof(ent->ent_name)); 2641d3aed33SMarcel Moolenaar } 2651d3aed33SMarcel Moolenaar 2661d3aed33SMarcel Moolenaar g_free(buf); 2671d3aed33SMarcel Moolenaar return (tbl); 2681d3aed33SMarcel Moolenaar } 2691d3aed33SMarcel Moolenaar 2701d3aed33SMarcel Moolenaar static int 2711d3aed33SMarcel Moolenaar gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec) 2721d3aed33SMarcel Moolenaar { 2731d3aed33SMarcel Moolenaar 2741d3aed33SMarcel Moolenaar if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid)) 2751d3aed33SMarcel Moolenaar return (0); 2761d3aed33SMarcel Moolenaar return ((pri->hdr_revision == sec->hdr_revision && 2771d3aed33SMarcel Moolenaar pri->hdr_size == sec->hdr_size && 2781d3aed33SMarcel Moolenaar pri->hdr_lba_start == sec->hdr_lba_start && 2791d3aed33SMarcel Moolenaar pri->hdr_lba_end == sec->hdr_lba_end && 2801d3aed33SMarcel Moolenaar pri->hdr_entries == sec->hdr_entries && 2811d3aed33SMarcel Moolenaar pri->hdr_entsz == sec->hdr_entsz && 2821d3aed33SMarcel Moolenaar pri->hdr_crc_table == sec->hdr_crc_table) ? 1 : 0); 2831d3aed33SMarcel Moolenaar } 2841d3aed33SMarcel Moolenaar 2851d3aed33SMarcel Moolenaar static int 2861d3aed33SMarcel Moolenaar gpt_parse_type(const char *type, struct uuid *uuid) 2871d3aed33SMarcel Moolenaar { 2881d3aed33SMarcel Moolenaar struct uuid tmp; 289d287f590SMarcel Moolenaar const char *alias; 2901d3aed33SMarcel Moolenaar int error; 2911d3aed33SMarcel Moolenaar 292d287f590SMarcel Moolenaar if (type[0] == '!') { 293d287f590SMarcel Moolenaar error = parse_uuid(type + 1, &tmp); 2941d3aed33SMarcel Moolenaar if (error) 2951d3aed33SMarcel Moolenaar return (error); 2961d3aed33SMarcel Moolenaar if (EQUUID(&tmp, &gpt_uuid_unused)) 2971d3aed33SMarcel Moolenaar return (EINVAL); 2981d3aed33SMarcel Moolenaar *uuid = tmp; 2991d3aed33SMarcel Moolenaar return (0); 3001d3aed33SMarcel Moolenaar } 301d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_EFI); 302d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 3031d3aed33SMarcel Moolenaar *uuid = gpt_uuid_efi; 3041d3aed33SMarcel Moolenaar return (0); 3051d3aed33SMarcel Moolenaar } 306d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); 307d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 308d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd; 309d287f590SMarcel Moolenaar return (0); 310d287f590SMarcel Moolenaar } 311f352a0d4SJohn Baldwin alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT); 312f352a0d4SJohn Baldwin if (!strcasecmp(type, alias)) { 313f352a0d4SJohn Baldwin *uuid = gpt_uuid_freebsd_boot; 314f352a0d4SJohn Baldwin return (0); 315f352a0d4SJohn Baldwin } 316d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP); 317d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 318d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd_swap; 319d287f590SMarcel Moolenaar return (0); 320d287f590SMarcel Moolenaar } 321d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS); 322d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 323d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd_ufs; 324d287f590SMarcel Moolenaar return (0); 325d287f590SMarcel Moolenaar } 326d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM); 327d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 328d287f590SMarcel Moolenaar *uuid = gpt_uuid_freebsd_vinum; 329d287f590SMarcel Moolenaar return (0); 330d287f590SMarcel Moolenaar } 331a1fedf91SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS); 332a1fedf91SMarcel Moolenaar if (!strcasecmp(type, alias)) { 333a1fedf91SMarcel Moolenaar *uuid = gpt_uuid_freebsd_zfs; 334a1fedf91SMarcel Moolenaar return (0); 335a1fedf91SMarcel Moolenaar } 336d287f590SMarcel Moolenaar alias = g_part_alias_name(G_PART_ALIAS_MBR); 337d287f590SMarcel Moolenaar if (!strcasecmp(type, alias)) { 338d287f590SMarcel Moolenaar *uuid = gpt_uuid_mbr; 339d287f590SMarcel Moolenaar return (0); 340d287f590SMarcel Moolenaar } 341d7255ff4SRui Paulo alias = g_part_alias_name(G_PART_ALIAS_APPLE_HFS); 342d7255ff4SRui Paulo if (!strcasecmp(type, alias)) { 343d7255ff4SRui Paulo *uuid = gpt_uuid_apple_hfs; 344d7255ff4SRui Paulo return (0); 345d7255ff4SRui Paulo } 346d287f590SMarcel Moolenaar return (EINVAL); 347d287f590SMarcel Moolenaar } 3481d3aed33SMarcel Moolenaar 3491d3aed33SMarcel Moolenaar static int 3501d3aed33SMarcel Moolenaar g_part_gpt_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 3511d3aed33SMarcel Moolenaar struct g_part_parms *gpp) 3521d3aed33SMarcel Moolenaar { 3531d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 3541d3aed33SMarcel Moolenaar int error; 3551d3aed33SMarcel Moolenaar 3561d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 3571d3aed33SMarcel Moolenaar error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 3581d3aed33SMarcel Moolenaar if (error) 3591d3aed33SMarcel Moolenaar return (error); 3601d3aed33SMarcel Moolenaar kern_uuidgen(&entry->ent.ent_uuid, 1); 3611d3aed33SMarcel Moolenaar entry->ent.ent_lba_start = baseentry->gpe_start; 3621d3aed33SMarcel Moolenaar entry->ent.ent_lba_end = baseentry->gpe_end; 3631d3aed33SMarcel Moolenaar if (baseentry->gpe_deleted) { 3641d3aed33SMarcel Moolenaar entry->ent.ent_attr = 0; 3651d3aed33SMarcel Moolenaar bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 3661d3aed33SMarcel Moolenaar } 367d3532631SMarcel Moolenaar if (gpp->gpp_parms & G_PART_PARM_LABEL) 368d3532631SMarcel Moolenaar g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 369d3532631SMarcel Moolenaar sizeof(entry->ent.ent_name)); 3701d3aed33SMarcel Moolenaar return (0); 3711d3aed33SMarcel Moolenaar } 3721d3aed33SMarcel Moolenaar 3731d3aed33SMarcel Moolenaar static int 3744d32fcb4SMarcel Moolenaar g_part_gpt_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 3754d32fcb4SMarcel Moolenaar { 3764d32fcb4SMarcel Moolenaar struct g_part_gpt_table *table; 3774d32fcb4SMarcel Moolenaar 3784d32fcb4SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 3794d32fcb4SMarcel Moolenaar bcopy(gpp->gpp_codeptr, table->mbr, DOSPARTOFF); 3804d32fcb4SMarcel Moolenaar return (0); 3814d32fcb4SMarcel Moolenaar } 3824d32fcb4SMarcel Moolenaar 3834d32fcb4SMarcel Moolenaar static int 3841d3aed33SMarcel Moolenaar g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp) 3851d3aed33SMarcel Moolenaar { 3861d3aed33SMarcel Moolenaar struct g_provider *pp; 3871d3aed33SMarcel Moolenaar struct g_part_gpt_table *table; 3881d3aed33SMarcel Moolenaar quad_t last; 3891d3aed33SMarcel Moolenaar size_t tblsz; 3901d3aed33SMarcel Moolenaar 3911d3aed33SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 3921d3aed33SMarcel Moolenaar pp = gpp->gpp_provider; 3931d3aed33SMarcel Moolenaar tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) + 3941d3aed33SMarcel Moolenaar pp->sectorsize - 1) / pp->sectorsize; 3954d32fcb4SMarcel Moolenaar if (pp->sectorsize < MBRSIZE || 3961d3aed33SMarcel Moolenaar pp->mediasize < (3 + 2 * tblsz + basetable->gpt_entries) * 3971d3aed33SMarcel Moolenaar pp->sectorsize) 3981d3aed33SMarcel Moolenaar return (ENOSPC); 3991d3aed33SMarcel Moolenaar 4001d3aed33SMarcel Moolenaar last = (pp->mediasize / pp->sectorsize) - 1; 4011d3aed33SMarcel Moolenaar 4024d32fcb4SMarcel Moolenaar le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); 4034d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 1] = 0xff; /* shd */ 4044d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 2] = 0xff; /* ssect */ 4054d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 3] = 0xff; /* scyl */ 4064d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 4] = 0xee; /* typ */ 4074d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 5] = 0xff; /* ehd */ 4084d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 6] = 0xff; /* esect */ 4094d32fcb4SMarcel Moolenaar table->mbr[DOSPARTOFF + 7] = 0xff; /* ecyl */ 4104d32fcb4SMarcel Moolenaar le32enc(table->mbr + DOSPARTOFF + 8, 1); /* start */ 4114d32fcb4SMarcel Moolenaar le32enc(table->mbr + DOSPARTOFF + 12, MIN(last, 0xffffffffLL)); 4124d32fcb4SMarcel Moolenaar 4131d3aed33SMarcel Moolenaar table->lba[GPT_ELT_PRIHDR] = 1; 4141d3aed33SMarcel Moolenaar table->lba[GPT_ELT_PRITBL] = 2; 4151d3aed33SMarcel Moolenaar table->lba[GPT_ELT_SECHDR] = last; 4161d3aed33SMarcel Moolenaar table->lba[GPT_ELT_SECTBL] = last - tblsz; 4171d3aed33SMarcel Moolenaar 4181d3aed33SMarcel Moolenaar bcopy(GPT_HDR_SIG, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig)); 4191d3aed33SMarcel Moolenaar table->hdr.hdr_revision = GPT_HDR_REVISION; 4201d3aed33SMarcel Moolenaar table->hdr.hdr_size = offsetof(struct gpt_hdr, padding); 4211d3aed33SMarcel Moolenaar table->hdr.hdr_lba_start = 2 + tblsz; 4221d3aed33SMarcel Moolenaar table->hdr.hdr_lba_end = last - tblsz - 1; 4231d3aed33SMarcel Moolenaar kern_uuidgen(&table->hdr.hdr_uuid, 1); 4241d3aed33SMarcel Moolenaar table->hdr.hdr_entries = basetable->gpt_entries; 4251d3aed33SMarcel Moolenaar table->hdr.hdr_entsz = sizeof(struct gpt_ent); 4261d3aed33SMarcel Moolenaar 4271d3aed33SMarcel Moolenaar basetable->gpt_first = table->hdr.hdr_lba_start; 4281d3aed33SMarcel Moolenaar basetable->gpt_last = table->hdr.hdr_lba_end; 4291d3aed33SMarcel Moolenaar return (0); 4301d3aed33SMarcel Moolenaar } 4311d3aed33SMarcel Moolenaar 4321d3aed33SMarcel Moolenaar static int 4331d3aed33SMarcel Moolenaar g_part_gpt_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 4341d3aed33SMarcel Moolenaar { 4351d3aed33SMarcel Moolenaar 4361d3aed33SMarcel Moolenaar /* 4371d3aed33SMarcel Moolenaar * Wipe the first 2 sectors as well as the last to clear the 4381d3aed33SMarcel Moolenaar * partitioning. 4391d3aed33SMarcel Moolenaar */ 4401d3aed33SMarcel Moolenaar basetable->gpt_smhead |= 3; 4411d3aed33SMarcel Moolenaar basetable->gpt_smtail |= 1; 4421d3aed33SMarcel Moolenaar return (0); 4431d3aed33SMarcel Moolenaar } 4441d3aed33SMarcel Moolenaar 4451d3aed33SMarcel Moolenaar static int 4465db67052SMarcel Moolenaar g_part_gpt_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 4475db67052SMarcel Moolenaar struct sbuf *sb, const char *indent) 4485db67052SMarcel Moolenaar { 4495db67052SMarcel Moolenaar struct g_part_gpt_entry *entry; 4505db67052SMarcel Moolenaar 4515db67052SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 4520c132595SMarcel Moolenaar if (indent == NULL) { 4530c132595SMarcel Moolenaar /* conftxt: libdisk compatibility */ 4545db67052SMarcel Moolenaar sbuf_printf(sb, " xs GPT xt "); 4555db67052SMarcel Moolenaar sbuf_printf_uuid(sb, &entry->ent.ent_type); 4560c132595SMarcel Moolenaar } else if (entry != NULL) { 4570c132595SMarcel Moolenaar /* confxml: partition entry information */ 458d3532631SMarcel Moolenaar sbuf_printf(sb, "%s<label>", indent); 459d3532631SMarcel Moolenaar g_gpt_printf_utf16(sb, entry->ent.ent_name, 460d3532631SMarcel Moolenaar sizeof(entry->ent.ent_name) >> 1); 461d3532631SMarcel Moolenaar sbuf_printf(sb, "</label>\n"); 4620c132595SMarcel Moolenaar sbuf_printf(sb, "%s<rawtype>", indent); 4630c132595SMarcel Moolenaar sbuf_printf_uuid(sb, &entry->ent.ent_type); 4640c132595SMarcel Moolenaar sbuf_printf(sb, "</rawtype>\n"); 4650c132595SMarcel Moolenaar } else { 4660c132595SMarcel Moolenaar /* confxml: scheme information */ 4670c132595SMarcel Moolenaar } 4685db67052SMarcel Moolenaar return (0); 4695db67052SMarcel Moolenaar } 4705db67052SMarcel Moolenaar 4715db67052SMarcel Moolenaar static int 4721d3aed33SMarcel Moolenaar g_part_gpt_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 4731d3aed33SMarcel Moolenaar { 4741d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 4751d3aed33SMarcel Moolenaar 4761d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 4771d3aed33SMarcel Moolenaar return ((EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd_swap) || 4781d3aed33SMarcel Moolenaar EQUUID(&entry->ent.ent_type, &gpt_uuid_linux_swap)) ? 1 : 0); 4791d3aed33SMarcel Moolenaar } 4801d3aed33SMarcel Moolenaar 4811d3aed33SMarcel Moolenaar static int 4821d3aed33SMarcel Moolenaar g_part_gpt_modify(struct g_part_table *basetable, 4831d3aed33SMarcel Moolenaar struct g_part_entry *baseentry, struct g_part_parms *gpp) 4841d3aed33SMarcel Moolenaar { 4851d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 4861d3aed33SMarcel Moolenaar int error; 4871d3aed33SMarcel Moolenaar 4881d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 489d287f590SMarcel Moolenaar if (gpp->gpp_parms & G_PART_PARM_TYPE) { 4901d3aed33SMarcel Moolenaar error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 4911d3aed33SMarcel Moolenaar if (error) 4921d3aed33SMarcel Moolenaar return (error); 493d287f590SMarcel Moolenaar } 494d3532631SMarcel Moolenaar if (gpp->gpp_parms & G_PART_PARM_LABEL) 495d3532631SMarcel Moolenaar g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 496d3532631SMarcel Moolenaar sizeof(entry->ent.ent_name)); 4971d3aed33SMarcel Moolenaar return (0); 4981d3aed33SMarcel Moolenaar } 4991d3aed33SMarcel Moolenaar 5001d3aed33SMarcel Moolenaar static char * 5011d3aed33SMarcel Moolenaar g_part_gpt_name(struct g_part_table *table, struct g_part_entry *baseentry, 5021d3aed33SMarcel Moolenaar char *buf, size_t bufsz) 5031d3aed33SMarcel Moolenaar { 5041d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 5051d3aed33SMarcel Moolenaar char c; 5061d3aed33SMarcel Moolenaar 5071d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 5081d3aed33SMarcel Moolenaar c = (EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd)) ? 's' : 'p'; 5091d3aed33SMarcel Moolenaar snprintf(buf, bufsz, "%c%d", c, baseentry->gpe_index); 5101d3aed33SMarcel Moolenaar return (buf); 5111d3aed33SMarcel Moolenaar } 5121d3aed33SMarcel Moolenaar 5131d3aed33SMarcel Moolenaar static int 5141d3aed33SMarcel Moolenaar g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp) 5151d3aed33SMarcel Moolenaar { 5161d3aed33SMarcel Moolenaar struct g_provider *pp; 5171d3aed33SMarcel Moolenaar char *buf; 5181d3aed33SMarcel Moolenaar int error, res; 5191d3aed33SMarcel Moolenaar 5201d3aed33SMarcel Moolenaar /* We don't nest, which means that our depth should be 0. */ 5211d3aed33SMarcel Moolenaar if (table->gpt_depth != 0) 5221d3aed33SMarcel Moolenaar return (ENXIO); 5231d3aed33SMarcel Moolenaar 5241d3aed33SMarcel Moolenaar pp = cp->provider; 5251d3aed33SMarcel Moolenaar 5261d3aed33SMarcel Moolenaar /* 5271d3aed33SMarcel Moolenaar * Sanity-check the provider. Since the first sector on the provider 5281d3aed33SMarcel Moolenaar * must be a PMBR and a PMBR is 512 bytes large, the sector size 5291d3aed33SMarcel Moolenaar * must be at least 512 bytes. Also, since the theoretical minimum 5301d3aed33SMarcel Moolenaar * number of sectors needed by GPT is 6, any medium that has less 5311d3aed33SMarcel Moolenaar * than 6 sectors is never going to be able to hold a GPT. The 5321d3aed33SMarcel Moolenaar * number 6 comes from: 5331d3aed33SMarcel Moolenaar * 1 sector for the PMBR 5341d3aed33SMarcel Moolenaar * 2 sectors for the GPT headers (each 1 sector) 5351d3aed33SMarcel Moolenaar * 2 sectors for the GPT tables (each 1 sector) 5361d3aed33SMarcel Moolenaar * 1 sector for an actual partition 5371d3aed33SMarcel Moolenaar * It's better to catch this pathological case early than behaving 5381d3aed33SMarcel Moolenaar * pathologically later on... 5391d3aed33SMarcel Moolenaar */ 5404d32fcb4SMarcel Moolenaar if (pp->sectorsize < MBRSIZE || pp->mediasize < 6 * pp->sectorsize) 5411d3aed33SMarcel Moolenaar return (ENOSPC); 5421d3aed33SMarcel Moolenaar 5431d3aed33SMarcel Moolenaar /* Check that there's a MBR. */ 5441d3aed33SMarcel Moolenaar buf = g_read_data(cp, 0L, pp->sectorsize, &error); 5451d3aed33SMarcel Moolenaar if (buf == NULL) 5461d3aed33SMarcel Moolenaar return (error); 5471d3aed33SMarcel Moolenaar res = le16dec(buf + DOSMAGICOFFSET); 5481d3aed33SMarcel Moolenaar g_free(buf); 5491d3aed33SMarcel Moolenaar if (res != DOSMAGIC) 5501d3aed33SMarcel Moolenaar return (ENXIO); 5511d3aed33SMarcel Moolenaar 5521d3aed33SMarcel Moolenaar /* Check that there's a primary header. */ 5531d3aed33SMarcel Moolenaar buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); 5541d3aed33SMarcel Moolenaar if (buf == NULL) 5551d3aed33SMarcel Moolenaar return (error); 5561d3aed33SMarcel Moolenaar res = memcmp(buf, GPT_HDR_SIG, 8); 5571d3aed33SMarcel Moolenaar g_free(buf); 5581d3aed33SMarcel Moolenaar if (res == 0) 5591d3aed33SMarcel Moolenaar return (G_PART_PROBE_PRI_HIGH); 5601d3aed33SMarcel Moolenaar 5611d3aed33SMarcel Moolenaar /* No primary? Check that there's a secondary. */ 5621d3aed33SMarcel Moolenaar buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 5631d3aed33SMarcel Moolenaar &error); 5641d3aed33SMarcel Moolenaar if (buf == NULL) 5651d3aed33SMarcel Moolenaar return (error); 5661d3aed33SMarcel Moolenaar res = memcmp(buf, GPT_HDR_SIG, 8); 5671d3aed33SMarcel Moolenaar g_free(buf); 5681d3aed33SMarcel Moolenaar return ((res == 0) ? G_PART_PROBE_PRI_HIGH : ENXIO); 5691d3aed33SMarcel Moolenaar } 5701d3aed33SMarcel Moolenaar 5711d3aed33SMarcel Moolenaar static int 5721d3aed33SMarcel Moolenaar g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp) 5731d3aed33SMarcel Moolenaar { 5741d3aed33SMarcel Moolenaar struct gpt_hdr prihdr, sechdr; 5751d3aed33SMarcel Moolenaar struct gpt_ent *tbl, *pritbl, *sectbl; 5761d3aed33SMarcel Moolenaar struct g_provider *pp; 5771d3aed33SMarcel Moolenaar struct g_part_gpt_table *table; 5781d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 5794d32fcb4SMarcel Moolenaar u_char *buf; 5804d32fcb4SMarcel Moolenaar int error, index; 5811d3aed33SMarcel Moolenaar 5821d3aed33SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 5831d3aed33SMarcel Moolenaar pp = cp->provider; 5841d3aed33SMarcel Moolenaar 5854d32fcb4SMarcel Moolenaar /* Read the PMBR */ 5864d32fcb4SMarcel Moolenaar buf = g_read_data(cp, 0, pp->sectorsize, &error); 5874d32fcb4SMarcel Moolenaar if (buf == NULL) 5884d32fcb4SMarcel Moolenaar return (error); 5894d32fcb4SMarcel Moolenaar bcopy(buf, table->mbr, MBRSIZE); 5904d32fcb4SMarcel Moolenaar g_free(buf); 5914d32fcb4SMarcel Moolenaar 5921d3aed33SMarcel Moolenaar /* Read the primary header and table. */ 5931d3aed33SMarcel Moolenaar gpt_read_hdr(table, cp, GPT_ELT_PRIHDR, &prihdr); 5941d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) { 5951d3aed33SMarcel Moolenaar pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, &prihdr); 5961d3aed33SMarcel Moolenaar } else { 5971d3aed33SMarcel Moolenaar table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 5981d3aed33SMarcel Moolenaar pritbl = NULL; 5991d3aed33SMarcel Moolenaar } 6001d3aed33SMarcel Moolenaar 6011d3aed33SMarcel Moolenaar /* Read the secondary header and table. */ 6021d3aed33SMarcel Moolenaar gpt_read_hdr(table, cp, GPT_ELT_SECHDR, &sechdr); 6031d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) { 6041d3aed33SMarcel Moolenaar sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, &sechdr); 6051d3aed33SMarcel Moolenaar } else { 6061d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 6071d3aed33SMarcel Moolenaar sectbl = NULL; 6081d3aed33SMarcel Moolenaar } 6091d3aed33SMarcel Moolenaar 6101d3aed33SMarcel Moolenaar /* Fail if we haven't got any good tables at all. */ 6111d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK && 6121d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 6131d3aed33SMarcel Moolenaar printf("GEOM: %s: corrupt or invalid GPT detected.\n", 6141d3aed33SMarcel Moolenaar pp->name); 6151d3aed33SMarcel Moolenaar printf("GEOM: %s: GPT rejected -- may not be recoverable.\n", 6161d3aed33SMarcel Moolenaar pp->name); 6171d3aed33SMarcel Moolenaar return (EINVAL); 6181d3aed33SMarcel Moolenaar } 6191d3aed33SMarcel Moolenaar 6201d3aed33SMarcel Moolenaar /* 6211d3aed33SMarcel Moolenaar * If both headers are good but they disagree with each other, 6221d3aed33SMarcel Moolenaar * then invalidate one. We prefer to keep the primary header, 6231d3aed33SMarcel Moolenaar * unless the primary table is corrupt. 6241d3aed33SMarcel Moolenaar */ 6251d3aed33SMarcel Moolenaar if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK && 6261d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECHDR] == GPT_STATE_OK && 6271d3aed33SMarcel Moolenaar !gpt_matched_hdrs(&prihdr, &sechdr)) { 628dd0db05dSMarcel Moolenaar if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK) { 6291d3aed33SMarcel Moolenaar table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID; 630dd0db05dSMarcel Moolenaar table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 631dd0db05dSMarcel Moolenaar } else { 6321d3aed33SMarcel Moolenaar table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID; 633dd0db05dSMarcel Moolenaar table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 634dd0db05dSMarcel Moolenaar } 6351d3aed33SMarcel Moolenaar } 6361d3aed33SMarcel Moolenaar 637dd0db05dSMarcel Moolenaar if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK) { 6381d3aed33SMarcel Moolenaar printf("GEOM: %s: the primary GPT table is corrupt or " 6391d3aed33SMarcel Moolenaar "invalid.\n", pp->name); 6401d3aed33SMarcel Moolenaar printf("GEOM: %s: using the secondary instead -- recovery " 6411d3aed33SMarcel Moolenaar "strongly advised.\n", pp->name); 6421d3aed33SMarcel Moolenaar table->hdr = sechdr; 6431d3aed33SMarcel Moolenaar tbl = sectbl; 6441d3aed33SMarcel Moolenaar if (pritbl != NULL) 6451d3aed33SMarcel Moolenaar g_free(pritbl); 6461d3aed33SMarcel Moolenaar } else { 647dd0db05dSMarcel Moolenaar if (table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 6481d3aed33SMarcel Moolenaar printf("GEOM: %s: the secondary GPT table is corrupt " 6491d3aed33SMarcel Moolenaar "or invalid.\n", pp->name); 6501d3aed33SMarcel Moolenaar printf("GEOM: %s: using the primary only -- recovery " 6511d3aed33SMarcel Moolenaar "suggested.\n", pp->name); 6521d3aed33SMarcel Moolenaar } 6531d3aed33SMarcel Moolenaar table->hdr = prihdr; 6541d3aed33SMarcel Moolenaar tbl = pritbl; 6551d3aed33SMarcel Moolenaar if (sectbl != NULL) 6561d3aed33SMarcel Moolenaar g_free(sectbl); 6571d3aed33SMarcel Moolenaar } 6581d3aed33SMarcel Moolenaar 6591d3aed33SMarcel Moolenaar basetable->gpt_first = table->hdr.hdr_lba_start; 6601d3aed33SMarcel Moolenaar basetable->gpt_last = table->hdr.hdr_lba_end; 6611d3aed33SMarcel Moolenaar basetable->gpt_entries = table->hdr.hdr_entries; 6621d3aed33SMarcel Moolenaar 6631d3aed33SMarcel Moolenaar for (index = basetable->gpt_entries - 1; index >= 0; index--) { 6641d3aed33SMarcel Moolenaar if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused)) 6651d3aed33SMarcel Moolenaar continue; 6661d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)g_part_new_entry(basetable, 6671d3aed33SMarcel Moolenaar index+1, tbl[index].ent_lba_start, tbl[index].ent_lba_end); 6681d3aed33SMarcel Moolenaar entry->ent = tbl[index]; 6691d3aed33SMarcel Moolenaar } 6701d3aed33SMarcel Moolenaar 6711d3aed33SMarcel Moolenaar g_free(tbl); 6721d3aed33SMarcel Moolenaar return (0); 6731d3aed33SMarcel Moolenaar } 6741d3aed33SMarcel Moolenaar 6751d3aed33SMarcel Moolenaar static const char * 6761d3aed33SMarcel Moolenaar g_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 6771d3aed33SMarcel Moolenaar char *buf, size_t bufsz) 6781d3aed33SMarcel Moolenaar { 6791d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 6801d3aed33SMarcel Moolenaar struct uuid *type; 6811d3aed33SMarcel Moolenaar 6821d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 6831d3aed33SMarcel Moolenaar type = &entry->ent.ent_type; 6841d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_efi)) 6851d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_EFI)); 6861d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd)) 6871d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); 688f352a0d4SJohn Baldwin if (EQUUID(type, &gpt_uuid_freebsd_boot)) 689f352a0d4SJohn Baldwin return (g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT)); 6901d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_swap)) 6911d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP)); 6921d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_ufs)) 6931d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS)); 6941d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_vinum)) 6951d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM)); 696a1fedf91SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_freebsd_zfs)) 697a1fedf91SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS)); 6981d3aed33SMarcel Moolenaar if (EQUUID(type, &gpt_uuid_mbr)) 6991d3aed33SMarcel Moolenaar return (g_part_alias_name(G_PART_ALIAS_MBR)); 700cf231470SMarcel Moolenaar buf[0] = '!'; 701cf231470SMarcel Moolenaar snprintf_uuid(buf + 1, bufsz - 1, type); 7021d3aed33SMarcel Moolenaar return (buf); 7031d3aed33SMarcel Moolenaar } 7041d3aed33SMarcel Moolenaar 7051d3aed33SMarcel Moolenaar static int 7061d3aed33SMarcel Moolenaar g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp) 7071d3aed33SMarcel Moolenaar { 7081d3aed33SMarcel Moolenaar unsigned char *buf, *bp; 7091d3aed33SMarcel Moolenaar struct g_provider *pp; 7101d3aed33SMarcel Moolenaar struct g_part_entry *baseentry; 7111d3aed33SMarcel Moolenaar struct g_part_gpt_entry *entry; 7121d3aed33SMarcel Moolenaar struct g_part_gpt_table *table; 7131d3aed33SMarcel Moolenaar size_t tlbsz; 7141d3aed33SMarcel Moolenaar uint32_t crc; 7151d3aed33SMarcel Moolenaar int error, index; 7161d3aed33SMarcel Moolenaar 7171d3aed33SMarcel Moolenaar pp = cp->provider; 7181d3aed33SMarcel Moolenaar table = (struct g_part_gpt_table *)basetable; 7191d3aed33SMarcel Moolenaar tlbsz = (table->hdr.hdr_entries * table->hdr.hdr_entsz + 7201d3aed33SMarcel Moolenaar pp->sectorsize - 1) / pp->sectorsize; 7211d3aed33SMarcel Moolenaar 7224d32fcb4SMarcel Moolenaar /* Write the PMBR */ 7231d3aed33SMarcel Moolenaar buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 7244d32fcb4SMarcel Moolenaar bcopy(table->mbr, buf, MBRSIZE); 7251d3aed33SMarcel Moolenaar error = g_write_data(cp, 0, buf, pp->sectorsize); 7261d3aed33SMarcel Moolenaar g_free(buf); 7271d3aed33SMarcel Moolenaar if (error) 7281d3aed33SMarcel Moolenaar return (error); 7291d3aed33SMarcel Moolenaar 7301d3aed33SMarcel Moolenaar /* Allocate space for the header and entries. */ 7311d3aed33SMarcel Moolenaar buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO); 7321d3aed33SMarcel Moolenaar 7331d3aed33SMarcel Moolenaar memcpy(buf, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig)); 7341d3aed33SMarcel Moolenaar le32enc(buf + 8, table->hdr.hdr_revision); 7351d3aed33SMarcel Moolenaar le32enc(buf + 12, table->hdr.hdr_size); 7361d3aed33SMarcel Moolenaar le64enc(buf + 40, table->hdr.hdr_lba_start); 7371d3aed33SMarcel Moolenaar le64enc(buf + 48, table->hdr.hdr_lba_end); 7381d3aed33SMarcel Moolenaar le_uuid_enc(buf + 56, &table->hdr.hdr_uuid); 7391d3aed33SMarcel Moolenaar le32enc(buf + 80, table->hdr.hdr_entries); 7401d3aed33SMarcel Moolenaar le32enc(buf + 84, table->hdr.hdr_entsz); 7411d3aed33SMarcel Moolenaar 7421d3aed33SMarcel Moolenaar LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { 743d287f590SMarcel Moolenaar if (baseentry->gpe_deleted) 744d287f590SMarcel Moolenaar continue; 7451d3aed33SMarcel Moolenaar entry = (struct g_part_gpt_entry *)baseentry; 7461d3aed33SMarcel Moolenaar index = baseentry->gpe_index - 1; 7471d3aed33SMarcel Moolenaar bp = buf + pp->sectorsize + table->hdr.hdr_entsz * index; 7481d3aed33SMarcel Moolenaar le_uuid_enc(bp, &entry->ent.ent_type); 7491d3aed33SMarcel Moolenaar le_uuid_enc(bp + 16, &entry->ent.ent_uuid); 7501d3aed33SMarcel Moolenaar le64enc(bp + 32, entry->ent.ent_lba_start); 7511d3aed33SMarcel Moolenaar le64enc(bp + 40, entry->ent.ent_lba_end); 7521d3aed33SMarcel Moolenaar le64enc(bp + 48, entry->ent.ent_attr); 7531d3aed33SMarcel Moolenaar memcpy(bp + 56, entry->ent.ent_name, 7541d3aed33SMarcel Moolenaar sizeof(entry->ent.ent_name)); 7551d3aed33SMarcel Moolenaar } 7561d3aed33SMarcel Moolenaar 7571d3aed33SMarcel Moolenaar crc = crc32(buf + pp->sectorsize, 7581d3aed33SMarcel Moolenaar table->hdr.hdr_entries * table->hdr.hdr_entsz); 7591d3aed33SMarcel Moolenaar le32enc(buf + 88, crc); 7601d3aed33SMarcel Moolenaar 7611d3aed33SMarcel Moolenaar /* Write primary meta-data. */ 7621d3aed33SMarcel Moolenaar le32enc(buf + 16, 0); /* hdr_crc_self. */ 7631d3aed33SMarcel Moolenaar le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_self. */ 7641d3aed33SMarcel Moolenaar le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_alt. */ 7651d3aed33SMarcel Moolenaar le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]); /* hdr_lba_table. */ 7661d3aed33SMarcel Moolenaar crc = crc32(buf, table->hdr.hdr_size); 7671d3aed33SMarcel Moolenaar le32enc(buf + 16, crc); 7681d3aed33SMarcel Moolenaar 7691d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize, 7701d3aed33SMarcel Moolenaar buf + pp->sectorsize, tlbsz * pp->sectorsize); 7711d3aed33SMarcel Moolenaar if (error) 7721d3aed33SMarcel Moolenaar goto out; 7731d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize, 7741d3aed33SMarcel Moolenaar buf, pp->sectorsize); 7751d3aed33SMarcel Moolenaar if (error) 7761d3aed33SMarcel Moolenaar goto out; 7771d3aed33SMarcel Moolenaar 7781d3aed33SMarcel Moolenaar /* Write secondary meta-data. */ 7791d3aed33SMarcel Moolenaar le32enc(buf + 16, 0); /* hdr_crc_self. */ 7801d3aed33SMarcel Moolenaar le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_self. */ 7811d3aed33SMarcel Moolenaar le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_alt. */ 7821d3aed33SMarcel Moolenaar le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]); /* hdr_lba_table. */ 7831d3aed33SMarcel Moolenaar crc = crc32(buf, table->hdr.hdr_size); 7841d3aed33SMarcel Moolenaar le32enc(buf + 16, crc); 7851d3aed33SMarcel Moolenaar 7861d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize, 7871d3aed33SMarcel Moolenaar buf + pp->sectorsize, tlbsz * pp->sectorsize); 7881d3aed33SMarcel Moolenaar if (error) 7891d3aed33SMarcel Moolenaar goto out; 7901d3aed33SMarcel Moolenaar error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize, 7911d3aed33SMarcel Moolenaar buf, pp->sectorsize); 7921d3aed33SMarcel Moolenaar 7931d3aed33SMarcel Moolenaar out: 7941d3aed33SMarcel Moolenaar g_free(buf); 7951d3aed33SMarcel Moolenaar return (error); 7961d3aed33SMarcel Moolenaar } 7971d3aed33SMarcel Moolenaar 7981d3aed33SMarcel Moolenaar static void 799d3532631SMarcel Moolenaar g_gpt_printf_utf16(struct sbuf *sb, uint16_t *str, size_t len) 8001d3aed33SMarcel Moolenaar { 8011d3aed33SMarcel Moolenaar u_int bo; 8021d3aed33SMarcel Moolenaar uint32_t ch; 8031d3aed33SMarcel Moolenaar uint16_t c; 8041d3aed33SMarcel Moolenaar 805d3532631SMarcel Moolenaar bo = LITTLE_ENDIAN; /* GPT is little-endian */ 8061d3aed33SMarcel Moolenaar while (len > 0 && *str != 0) { 8071d3aed33SMarcel Moolenaar ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str); 8081d3aed33SMarcel Moolenaar str++, len--; 8091d3aed33SMarcel Moolenaar if ((ch & 0xf800) == 0xd800) { 8101d3aed33SMarcel Moolenaar if (len > 0) { 8111d3aed33SMarcel Moolenaar c = (bo == BIG_ENDIAN) ? be16toh(*str) 8121d3aed33SMarcel Moolenaar : le16toh(*str); 8131d3aed33SMarcel Moolenaar str++, len--; 8141d3aed33SMarcel Moolenaar } else 8151d3aed33SMarcel Moolenaar c = 0xfffd; 8161d3aed33SMarcel Moolenaar if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) { 8171d3aed33SMarcel Moolenaar ch = ((ch & 0x3ff) << 10) + (c & 0x3ff); 8181d3aed33SMarcel Moolenaar ch += 0x10000; 8191d3aed33SMarcel Moolenaar } else 8201d3aed33SMarcel Moolenaar ch = 0xfffd; 8211d3aed33SMarcel Moolenaar } else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */ 8221d3aed33SMarcel Moolenaar bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN; 8231d3aed33SMarcel Moolenaar continue; 8241d3aed33SMarcel Moolenaar } else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */ 8251d3aed33SMarcel Moolenaar continue; 8261d3aed33SMarcel Moolenaar 827d3532631SMarcel Moolenaar /* Write the Unicode character in UTF-8 */ 8281d3aed33SMarcel Moolenaar if (ch < 0x80) 8291d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c", ch); 8301d3aed33SMarcel Moolenaar else if (ch < 0x800) 8311d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6), 8321d3aed33SMarcel Moolenaar 0x80 | (ch & 0x3f)); 8331d3aed33SMarcel Moolenaar else if (ch < 0x10000) 8341d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12), 8351d3aed33SMarcel Moolenaar 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 8361d3aed33SMarcel Moolenaar else if (ch < 0x200000) 8371d3aed33SMarcel Moolenaar sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18), 8381d3aed33SMarcel Moolenaar 0x80 | ((ch >> 12) & 0x3f), 8391d3aed33SMarcel Moolenaar 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 8401d3aed33SMarcel Moolenaar } 8411d3aed33SMarcel Moolenaar } 842d3532631SMarcel Moolenaar 843d3532631SMarcel Moolenaar static void 844d3532631SMarcel Moolenaar g_gpt_utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) 845d3532631SMarcel Moolenaar { 846d3532631SMarcel Moolenaar size_t s16idx, s8idx; 847d3532631SMarcel Moolenaar uint32_t utfchar; 848d3532631SMarcel Moolenaar unsigned int c, utfbytes; 849d3532631SMarcel Moolenaar 850d3532631SMarcel Moolenaar s8idx = s16idx = 0; 851d3532631SMarcel Moolenaar utfchar = 0; 852d3532631SMarcel Moolenaar utfbytes = 0; 853d3532631SMarcel Moolenaar bzero(s16, s16len << 1); 854d3532631SMarcel Moolenaar while (s8[s8idx] != 0 && s16idx < s16len) { 855d3532631SMarcel Moolenaar c = s8[s8idx++]; 856d3532631SMarcel Moolenaar if ((c & 0xc0) != 0x80) { 857d3532631SMarcel Moolenaar /* Initial characters. */ 858d3532631SMarcel Moolenaar if (utfbytes != 0) { 859d3532631SMarcel Moolenaar /* Incomplete encoding of previous char. */ 860d3532631SMarcel Moolenaar s16[s16idx++] = htole16(0xfffd); 861d3532631SMarcel Moolenaar } 862d3532631SMarcel Moolenaar if ((c & 0xf8) == 0xf0) { 863d3532631SMarcel Moolenaar utfchar = c & 0x07; 864d3532631SMarcel Moolenaar utfbytes = 3; 865d3532631SMarcel Moolenaar } else if ((c & 0xf0) == 0xe0) { 866d3532631SMarcel Moolenaar utfchar = c & 0x0f; 867d3532631SMarcel Moolenaar utfbytes = 2; 868d3532631SMarcel Moolenaar } else if ((c & 0xe0) == 0xc0) { 869d3532631SMarcel Moolenaar utfchar = c & 0x1f; 870d3532631SMarcel Moolenaar utfbytes = 1; 871d3532631SMarcel Moolenaar } else { 872d3532631SMarcel Moolenaar utfchar = c & 0x7f; 873d3532631SMarcel Moolenaar utfbytes = 0; 874d3532631SMarcel Moolenaar } 875d3532631SMarcel Moolenaar } else { 876d3532631SMarcel Moolenaar /* Followup characters. */ 877d3532631SMarcel Moolenaar if (utfbytes > 0) { 878d3532631SMarcel Moolenaar utfchar = (utfchar << 6) + (c & 0x3f); 879d3532631SMarcel Moolenaar utfbytes--; 880d3532631SMarcel Moolenaar } else if (utfbytes == 0) 881d3532631SMarcel Moolenaar utfbytes = ~0; 882d3532631SMarcel Moolenaar } 883d3532631SMarcel Moolenaar /* 884d3532631SMarcel Moolenaar * Write the complete Unicode character as UTF-16 when we 885d3532631SMarcel Moolenaar * have all the UTF-8 charactars collected. 886d3532631SMarcel Moolenaar */ 887d3532631SMarcel Moolenaar if (utfbytes == 0) { 888d3532631SMarcel Moolenaar /* 889d3532631SMarcel Moolenaar * If we need to write 2 UTF-16 characters, but 890d3532631SMarcel Moolenaar * we only have room for 1, then we truncate the 891d3532631SMarcel Moolenaar * string by writing a 0 instead. 892d3532631SMarcel Moolenaar */ 893d3532631SMarcel Moolenaar if (utfchar >= 0x10000 && s16idx < s16len - 1) { 894d3532631SMarcel Moolenaar s16[s16idx++] = 895d3532631SMarcel Moolenaar htole16(0xd800 | ((utfchar >> 10) - 0x40)); 896d3532631SMarcel Moolenaar s16[s16idx++] = 897d3532631SMarcel Moolenaar htole16(0xdc00 | (utfchar & 0x3ff)); 898d3532631SMarcel Moolenaar } else 899d3532631SMarcel Moolenaar s16[s16idx++] = (utfchar >= 0x10000) ? 0 : 900d3532631SMarcel Moolenaar htole16(utfchar); 901d3532631SMarcel Moolenaar } 902d3532631SMarcel Moolenaar } 903d3532631SMarcel Moolenaar /* 904d3532631SMarcel Moolenaar * If our input string was truncated, append an invalid encoding 905d3532631SMarcel Moolenaar * character to the output string. 906d3532631SMarcel Moolenaar */ 907d3532631SMarcel Moolenaar if (utfbytes != 0 && s16idx < s16len) 908d3532631SMarcel Moolenaar s16[s16idx++] = htole16(0xfffd); 909d3532631SMarcel Moolenaar } 910