g_part_gpt.c revision 223660
1/*- 2 * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/geom/part/g_part_gpt.c 223660 2011-06-29 05:41:14Z ae $"); 29 30#include <sys/param.h> 31#include <sys/bio.h> 32#include <sys/diskmbr.h> 33#include <sys/endian.h> 34#include <sys/gpt.h> 35#include <sys/kernel.h> 36#include <sys/kobj.h> 37#include <sys/limits.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/mutex.h> 41#include <sys/queue.h> 42#include <sys/sbuf.h> 43#include <sys/systm.h> 44#include <sys/sysctl.h> 45#include <sys/uuid.h> 46#include <geom/geom.h> 47#include <geom/part/g_part.h> 48 49#include "g_part_if.h" 50 51FEATURE(geom_part_gpt, "GEOM partitioning class for GPT partitions support"); 52 53CTASSERT(offsetof(struct gpt_hdr, padding) == 92); 54CTASSERT(sizeof(struct gpt_ent) == 128); 55 56#define EQUUID(a,b) (memcmp(a, b, sizeof(struct uuid)) == 0) 57 58#define MBRSIZE 512 59 60enum gpt_elt { 61 GPT_ELT_PRIHDR, 62 GPT_ELT_PRITBL, 63 GPT_ELT_SECHDR, 64 GPT_ELT_SECTBL, 65 GPT_ELT_COUNT 66}; 67 68enum gpt_state { 69 GPT_STATE_UNKNOWN, /* Not determined. */ 70 GPT_STATE_MISSING, /* No signature found. */ 71 GPT_STATE_CORRUPT, /* Checksum mismatch. */ 72 GPT_STATE_INVALID, /* Nonconformant/invalid. */ 73 GPT_STATE_OK /* Perfectly fine. */ 74}; 75 76struct g_part_gpt_table { 77 struct g_part_table base; 78 u_char mbr[MBRSIZE]; 79 struct gpt_hdr *hdr; 80 quad_t lba[GPT_ELT_COUNT]; 81 enum gpt_state state[GPT_ELT_COUNT]; 82}; 83 84struct g_part_gpt_entry { 85 struct g_part_entry base; 86 struct gpt_ent ent; 87}; 88 89static void g_gpt_printf_utf16(struct sbuf *, uint16_t *, size_t); 90static void g_gpt_utf8_to_utf16(const uint8_t *, uint16_t *, size_t); 91static void g_gpt_set_defaults(struct g_part_table *, struct g_provider *); 92 93static int g_part_gpt_add(struct g_part_table *, struct g_part_entry *, 94 struct g_part_parms *); 95static int g_part_gpt_bootcode(struct g_part_table *, struct g_part_parms *); 96static int g_part_gpt_create(struct g_part_table *, struct g_part_parms *); 97static int g_part_gpt_destroy(struct g_part_table *, struct g_part_parms *); 98static void g_part_gpt_dumpconf(struct g_part_table *, struct g_part_entry *, 99 struct sbuf *, const char *); 100static int g_part_gpt_dumpto(struct g_part_table *, struct g_part_entry *); 101static int g_part_gpt_modify(struct g_part_table *, struct g_part_entry *, 102 struct g_part_parms *); 103static const char *g_part_gpt_name(struct g_part_table *, struct g_part_entry *, 104 char *, size_t); 105static int g_part_gpt_probe(struct g_part_table *, struct g_consumer *); 106static int g_part_gpt_read(struct g_part_table *, struct g_consumer *); 107static int g_part_gpt_setunset(struct g_part_table *table, 108 struct g_part_entry *baseentry, const char *attrib, unsigned int set); 109static const char *g_part_gpt_type(struct g_part_table *, struct g_part_entry *, 110 char *, size_t); 111static int g_part_gpt_write(struct g_part_table *, struct g_consumer *); 112static int g_part_gpt_resize(struct g_part_table *, struct g_part_entry *, 113 struct g_part_parms *); 114static int g_part_gpt_recover(struct g_part_table *); 115 116static kobj_method_t g_part_gpt_methods[] = { 117 KOBJMETHOD(g_part_add, g_part_gpt_add), 118 KOBJMETHOD(g_part_bootcode, g_part_gpt_bootcode), 119 KOBJMETHOD(g_part_create, g_part_gpt_create), 120 KOBJMETHOD(g_part_destroy, g_part_gpt_destroy), 121 KOBJMETHOD(g_part_dumpconf, g_part_gpt_dumpconf), 122 KOBJMETHOD(g_part_dumpto, g_part_gpt_dumpto), 123 KOBJMETHOD(g_part_modify, g_part_gpt_modify), 124 KOBJMETHOD(g_part_resize, g_part_gpt_resize), 125 KOBJMETHOD(g_part_name, g_part_gpt_name), 126 KOBJMETHOD(g_part_probe, g_part_gpt_probe), 127 KOBJMETHOD(g_part_read, g_part_gpt_read), 128 KOBJMETHOD(g_part_recover, g_part_gpt_recover), 129 KOBJMETHOD(g_part_setunset, g_part_gpt_setunset), 130 KOBJMETHOD(g_part_type, g_part_gpt_type), 131 KOBJMETHOD(g_part_write, g_part_gpt_write), 132 { 0, 0 } 133}; 134 135static struct g_part_scheme g_part_gpt_scheme = { 136 "GPT", 137 g_part_gpt_methods, 138 sizeof(struct g_part_gpt_table), 139 .gps_entrysz = sizeof(struct g_part_gpt_entry), 140 .gps_minent = 128, 141 .gps_maxent = 4096, 142 .gps_bootcodesz = MBRSIZE, 143}; 144G_PART_SCHEME_DECLARE(g_part_gpt); 145 146static struct uuid gpt_uuid_apple_boot = GPT_ENT_TYPE_APPLE_BOOT; 147static struct uuid gpt_uuid_apple_hfs = GPT_ENT_TYPE_APPLE_HFS; 148static struct uuid gpt_uuid_apple_label = GPT_ENT_TYPE_APPLE_LABEL; 149static struct uuid gpt_uuid_apple_raid = GPT_ENT_TYPE_APPLE_RAID; 150static struct uuid gpt_uuid_apple_raid_offline = GPT_ENT_TYPE_APPLE_RAID_OFFLINE; 151static struct uuid gpt_uuid_apple_tv_recovery = GPT_ENT_TYPE_APPLE_TV_RECOVERY; 152static struct uuid gpt_uuid_apple_ufs = GPT_ENT_TYPE_APPLE_UFS; 153static struct uuid gpt_uuid_bios_boot = GPT_ENT_TYPE_BIOS_BOOT; 154static struct uuid gpt_uuid_efi = GPT_ENT_TYPE_EFI; 155static struct uuid gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; 156static struct uuid gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 157static struct uuid gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 158static struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 159static struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 160static struct uuid gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 161static struct uuid gpt_uuid_linux_data = GPT_ENT_TYPE_LINUX_DATA; 162static struct uuid gpt_uuid_linux_lvm = GPT_ENT_TYPE_LINUX_LVM; 163static struct uuid gpt_uuid_linux_raid = GPT_ENT_TYPE_LINUX_RAID; 164static struct uuid gpt_uuid_linux_swap = GPT_ENT_TYPE_LINUX_SWAP; 165static struct uuid gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 166static struct uuid gpt_uuid_ms_reserved = GPT_ENT_TYPE_MS_RESERVED; 167static struct uuid gpt_uuid_ms_ldm_data = GPT_ENT_TYPE_MS_LDM_DATA; 168static struct uuid gpt_uuid_ms_ldm_metadata = GPT_ENT_TYPE_MS_LDM_METADATA; 169static struct uuid gpt_uuid_netbsd_ccd = GPT_ENT_TYPE_NETBSD_CCD; 170static struct uuid gpt_uuid_netbsd_cgd = GPT_ENT_TYPE_NETBSD_CGD; 171static struct uuid gpt_uuid_netbsd_ffs = GPT_ENT_TYPE_NETBSD_FFS; 172static struct uuid gpt_uuid_netbsd_lfs = GPT_ENT_TYPE_NETBSD_LFS; 173static struct uuid gpt_uuid_netbsd_raid = GPT_ENT_TYPE_NETBSD_RAID; 174static struct uuid gpt_uuid_netbsd_swap = GPT_ENT_TYPE_NETBSD_SWAP; 175static struct uuid gpt_uuid_mbr = GPT_ENT_TYPE_MBR; 176static struct uuid gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 177 178static struct g_part_uuid_alias { 179 struct uuid *uuid; 180 int alias; 181} gpt_uuid_alias_match[] = { 182 { &gpt_uuid_apple_boot, G_PART_ALIAS_APPLE_BOOT }, 183 { &gpt_uuid_apple_hfs, G_PART_ALIAS_APPLE_HFS }, 184 { &gpt_uuid_apple_label, G_PART_ALIAS_APPLE_LABEL }, 185 { &gpt_uuid_apple_raid, G_PART_ALIAS_APPLE_RAID }, 186 { &gpt_uuid_apple_raid_offline, G_PART_ALIAS_APPLE_RAID_OFFLINE }, 187 { &gpt_uuid_apple_tv_recovery, G_PART_ALIAS_APPLE_TV_RECOVERY }, 188 { &gpt_uuid_apple_ufs, G_PART_ALIAS_APPLE_UFS }, 189 { &gpt_uuid_bios_boot, G_PART_ALIAS_BIOS_BOOT }, 190 { &gpt_uuid_efi, G_PART_ALIAS_EFI }, 191 { &gpt_uuid_freebsd, G_PART_ALIAS_FREEBSD }, 192 { &gpt_uuid_freebsd_boot, G_PART_ALIAS_FREEBSD_BOOT }, 193 { &gpt_uuid_freebsd_swap, G_PART_ALIAS_FREEBSD_SWAP }, 194 { &gpt_uuid_freebsd_ufs, G_PART_ALIAS_FREEBSD_UFS }, 195 { &gpt_uuid_freebsd_vinum, G_PART_ALIAS_FREEBSD_VINUM }, 196 { &gpt_uuid_freebsd_zfs, G_PART_ALIAS_FREEBSD_ZFS }, 197 { &gpt_uuid_linux_data, G_PART_ALIAS_LINUX_DATA }, 198 { &gpt_uuid_linux_lvm, G_PART_ALIAS_LINUX_LVM }, 199 { &gpt_uuid_linux_raid, G_PART_ALIAS_LINUX_RAID }, 200 { &gpt_uuid_linux_swap, G_PART_ALIAS_LINUX_SWAP }, 201 { &gpt_uuid_mbr, G_PART_ALIAS_MBR }, 202 { &gpt_uuid_ms_basic_data, G_PART_ALIAS_MS_BASIC_DATA }, 203 { &gpt_uuid_ms_ldm_data, G_PART_ALIAS_MS_LDM_DATA }, 204 { &gpt_uuid_ms_ldm_metadata, G_PART_ALIAS_MS_LDM_METADATA }, 205 { &gpt_uuid_ms_reserved, G_PART_ALIAS_MS_RESERVED }, 206 { &gpt_uuid_netbsd_ccd, G_PART_ALIAS_NETBSD_CCD }, 207 { &gpt_uuid_netbsd_cgd, G_PART_ALIAS_NETBSD_CGD }, 208 { &gpt_uuid_netbsd_ffs, G_PART_ALIAS_NETBSD_FFS }, 209 { &gpt_uuid_netbsd_lfs, G_PART_ALIAS_NETBSD_LFS }, 210 { &gpt_uuid_netbsd_raid, G_PART_ALIAS_NETBSD_RAID }, 211 { &gpt_uuid_netbsd_swap, G_PART_ALIAS_NETBSD_SWAP }, 212 213 { NULL, 0 } 214}; 215 216static struct gpt_hdr * 217gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp, 218 enum gpt_elt elt) 219{ 220 struct gpt_hdr *buf, *hdr; 221 struct g_provider *pp; 222 quad_t lba, last; 223 int error; 224 uint32_t crc, sz; 225 226 pp = cp->provider; 227 last = (pp->mediasize / pp->sectorsize) - 1; 228 table->state[elt] = GPT_STATE_MISSING; 229 /* 230 * If the primary header is valid look for secondary 231 * header in AlternateLBA, otherwise in the last medium's LBA. 232 */ 233 if (elt == GPT_ELT_SECHDR) { 234 if (table->state[GPT_ELT_PRIHDR] != GPT_STATE_OK) 235 table->lba[elt] = last; 236 } else 237 table->lba[elt] = 1; 238 buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize, 239 &error); 240 if (buf == NULL) 241 return (NULL); 242 hdr = NULL; 243 if (memcmp(buf->hdr_sig, GPT_HDR_SIG, sizeof(buf->hdr_sig)) != 0) 244 goto fail; 245 246 table->state[elt] = GPT_STATE_CORRUPT; 247 sz = le32toh(buf->hdr_size); 248 if (sz < 92 || sz > pp->sectorsize) 249 goto fail; 250 251 hdr = g_malloc(sz, M_WAITOK | M_ZERO); 252 bcopy(buf, hdr, sz); 253 hdr->hdr_size = sz; 254 255 crc = le32toh(buf->hdr_crc_self); 256 buf->hdr_crc_self = 0; 257 if (crc32(buf, sz) != crc) 258 goto fail; 259 hdr->hdr_crc_self = crc; 260 261 table->state[elt] = GPT_STATE_INVALID; 262 hdr->hdr_revision = le32toh(buf->hdr_revision); 263 if (hdr->hdr_revision < GPT_HDR_REVISION) 264 goto fail; 265 hdr->hdr_lba_self = le64toh(buf->hdr_lba_self); 266 if (hdr->hdr_lba_self != table->lba[elt]) 267 goto fail; 268 hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt); 269 if (hdr->hdr_lba_alt == hdr->hdr_lba_self || 270 hdr->hdr_lba_alt > last) 271 goto fail; 272 273 /* Check the managed area. */ 274 hdr->hdr_lba_start = le64toh(buf->hdr_lba_start); 275 if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last) 276 goto fail; 277 hdr->hdr_lba_end = le64toh(buf->hdr_lba_end); 278 if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last) 279 goto fail; 280 281 /* Check the table location and size of the table. */ 282 hdr->hdr_entries = le32toh(buf->hdr_entries); 283 hdr->hdr_entsz = le32toh(buf->hdr_entsz); 284 if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 || 285 (hdr->hdr_entsz & 7) != 0) 286 goto fail; 287 hdr->hdr_lba_table = le64toh(buf->hdr_lba_table); 288 if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last) 289 goto fail; 290 if (hdr->hdr_lba_table >= hdr->hdr_lba_start && 291 hdr->hdr_lba_table <= hdr->hdr_lba_end) 292 goto fail; 293 lba = hdr->hdr_lba_table + 294 (hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) / 295 pp->sectorsize - 1; 296 if (lba >= last) 297 goto fail; 298 if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end) 299 goto fail; 300 301 table->state[elt] = GPT_STATE_OK; 302 le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid); 303 hdr->hdr_crc_table = le32toh(buf->hdr_crc_table); 304 305 /* save LBA for secondary header */ 306 if (elt == GPT_ELT_PRIHDR) 307 table->lba[GPT_ELT_SECHDR] = hdr->hdr_lba_alt; 308 309 g_free(buf); 310 return (hdr); 311 312 fail: 313 if (hdr != NULL) 314 g_free(hdr); 315 g_free(buf); 316 return (NULL); 317} 318 319static struct gpt_ent * 320gpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp, 321 enum gpt_elt elt, struct gpt_hdr *hdr) 322{ 323 struct g_provider *pp; 324 struct gpt_ent *ent, *tbl; 325 char *buf, *p; 326 unsigned int idx, sectors, tblsz, size; 327 int error; 328 329 if (hdr == NULL) 330 return (NULL); 331 332 pp = cp->provider; 333 table->lba[elt] = hdr->hdr_lba_table; 334 335 table->state[elt] = GPT_STATE_MISSING; 336 tblsz = hdr->hdr_entries * hdr->hdr_entsz; 337 sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize; 338 buf = g_malloc(sectors * pp->sectorsize, M_WAITOK | M_ZERO); 339 for (idx = 0; idx < sectors; idx += MAXPHYS / pp->sectorsize) { 340 size = (sectors - idx > MAXPHYS / pp->sectorsize) ? MAXPHYS: 341 (sectors - idx) * pp->sectorsize; 342 p = g_read_data(cp, (table->lba[elt] + idx) * pp->sectorsize, 343 size, &error); 344 if (p == NULL) { 345 g_free(buf); 346 return (NULL); 347 } 348 bcopy(p, buf + idx * pp->sectorsize, size); 349 g_free(p); 350 } 351 table->state[elt] = GPT_STATE_CORRUPT; 352 if (crc32(buf, tblsz) != hdr->hdr_crc_table) { 353 g_free(buf); 354 return (NULL); 355 } 356 357 table->state[elt] = GPT_STATE_OK; 358 tbl = g_malloc(hdr->hdr_entries * sizeof(struct gpt_ent), 359 M_WAITOK | M_ZERO); 360 361 for (idx = 0, ent = tbl, p = buf; 362 idx < hdr->hdr_entries; 363 idx++, ent++, p += hdr->hdr_entsz) { 364 le_uuid_dec(p, &ent->ent_type); 365 le_uuid_dec(p + 16, &ent->ent_uuid); 366 ent->ent_lba_start = le64dec(p + 32); 367 ent->ent_lba_end = le64dec(p + 40); 368 ent->ent_attr = le64dec(p + 48); 369 /* Keep UTF-16 in little-endian. */ 370 bcopy(p + 56, ent->ent_name, sizeof(ent->ent_name)); 371 } 372 373 g_free(buf); 374 return (tbl); 375} 376 377static int 378gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec) 379{ 380 381 if (pri == NULL || sec == NULL) 382 return (0); 383 384 if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid)) 385 return (0); 386 return ((pri->hdr_revision == sec->hdr_revision && 387 pri->hdr_size == sec->hdr_size && 388 pri->hdr_lba_start == sec->hdr_lba_start && 389 pri->hdr_lba_end == sec->hdr_lba_end && 390 pri->hdr_entries == sec->hdr_entries && 391 pri->hdr_entsz == sec->hdr_entsz && 392 pri->hdr_crc_table == sec->hdr_crc_table) ? 1 : 0); 393} 394 395static int 396gpt_parse_type(const char *type, struct uuid *uuid) 397{ 398 struct uuid tmp; 399 const char *alias; 400 int error; 401 struct g_part_uuid_alias *uap; 402 403 if (type[0] == '!') { 404 error = parse_uuid(type + 1, &tmp); 405 if (error) 406 return (error); 407 if (EQUUID(&tmp, &gpt_uuid_unused)) 408 return (EINVAL); 409 *uuid = tmp; 410 return (0); 411 } 412 for (uap = &gpt_uuid_alias_match[0]; uap->uuid; uap++) { 413 alias = g_part_alias_name(uap->alias); 414 if (!strcasecmp(type, alias)) { 415 *uuid = *uap->uuid; 416 return (0); 417 } 418 } 419 return (EINVAL); 420} 421 422static int 423g_part_gpt_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 424 struct g_part_parms *gpp) 425{ 426 struct g_part_gpt_entry *entry; 427 int error; 428 429 entry = (struct g_part_gpt_entry *)baseentry; 430 error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 431 if (error) 432 return (error); 433 kern_uuidgen(&entry->ent.ent_uuid, 1); 434 entry->ent.ent_lba_start = baseentry->gpe_start; 435 entry->ent.ent_lba_end = baseentry->gpe_end; 436 if (baseentry->gpe_deleted) { 437 entry->ent.ent_attr = 0; 438 bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 439 } 440 if (gpp->gpp_parms & G_PART_PARM_LABEL) 441 g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 442 sizeof(entry->ent.ent_name) / 443 sizeof(entry->ent.ent_name[0])); 444 return (0); 445} 446 447static int 448g_part_gpt_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 449{ 450 struct g_part_gpt_table *table; 451 size_t codesz; 452 453 codesz = DOSPARTOFF; 454 table = (struct g_part_gpt_table *)basetable; 455 bzero(table->mbr, codesz); 456 codesz = MIN(codesz, gpp->gpp_codesize); 457 if (codesz > 0) 458 bcopy(gpp->gpp_codeptr, table->mbr, codesz); 459 460 /* Mark the PMBR active since some BIOS require it */ 461 table->mbr[DOSPARTOFF] = 0x80; /* status */ 462 return (0); 463} 464 465static int 466g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp) 467{ 468 struct g_provider *pp; 469 struct g_part_gpt_table *table; 470 quad_t last; 471 size_t tblsz; 472 473 /* We don't nest, which means that our depth should be 0. */ 474 if (basetable->gpt_depth != 0) 475 return (ENXIO); 476 477 table = (struct g_part_gpt_table *)basetable; 478 pp = gpp->gpp_provider; 479 tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) + 480 pp->sectorsize - 1) / pp->sectorsize; 481 if (pp->sectorsize < MBRSIZE || 482 pp->mediasize < (3 + 2 * tblsz + basetable->gpt_entries) * 483 pp->sectorsize) 484 return (ENOSPC); 485 486 last = (pp->mediasize / pp->sectorsize) - 1; 487 488 le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); 489 table->mbr[DOSPARTOFF + 1] = 0x01; /* shd */ 490 table->mbr[DOSPARTOFF + 2] = 0x01; /* ssect */ 491 table->mbr[DOSPARTOFF + 3] = 0x00; /* scyl */ 492 table->mbr[DOSPARTOFF + 4] = 0xee; /* typ */ 493 table->mbr[DOSPARTOFF + 5] = 0xff; /* ehd */ 494 table->mbr[DOSPARTOFF + 6] = 0xff; /* esect */ 495 table->mbr[DOSPARTOFF + 7] = 0xff; /* ecyl */ 496 le32enc(table->mbr + DOSPARTOFF + 8, 1); /* start */ 497 le32enc(table->mbr + DOSPARTOFF + 12, MIN(last, UINT32_MAX)); 498 499 /* Allocate space for the header */ 500 table->hdr = g_malloc(sizeof(struct gpt_hdr), M_WAITOK | M_ZERO); 501 502 bcopy(GPT_HDR_SIG, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); 503 table->hdr->hdr_revision = GPT_HDR_REVISION; 504 table->hdr->hdr_size = offsetof(struct gpt_hdr, padding); 505 kern_uuidgen(&table->hdr->hdr_uuid, 1); 506 table->hdr->hdr_entries = basetable->gpt_entries; 507 table->hdr->hdr_entsz = sizeof(struct gpt_ent); 508 509 g_gpt_set_defaults(basetable, pp); 510 return (0); 511} 512 513static int 514g_part_gpt_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 515{ 516 struct g_part_gpt_table *table; 517 struct g_provider *pp; 518 519 table = (struct g_part_gpt_table *)basetable; 520 pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 521 g_free(table->hdr); 522 table->hdr = NULL; 523 524 /* 525 * Wipe the first 2 sectors to clear the partitioning. Wipe the last 526 * sector only if it has valid secondary header. 527 */ 528 basetable->gpt_smhead |= 3; 529 if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK && 530 table->lba[GPT_ELT_SECHDR] == pp->mediasize / pp->sectorsize - 1) 531 basetable->gpt_smtail |= 1; 532 return (0); 533} 534 535static void 536g_part_gpt_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 537 struct sbuf *sb, const char *indent) 538{ 539 struct g_part_gpt_entry *entry; 540 541 entry = (struct g_part_gpt_entry *)baseentry; 542 if (indent == NULL) { 543 /* conftxt: libdisk compatibility */ 544 sbuf_printf(sb, " xs GPT xt "); 545 sbuf_printf_uuid(sb, &entry->ent.ent_type); 546 } else if (entry != NULL) { 547 /* confxml: partition entry information */ 548 sbuf_printf(sb, "%s<label>", indent); 549 g_gpt_printf_utf16(sb, entry->ent.ent_name, 550 sizeof(entry->ent.ent_name) >> 1); 551 sbuf_printf(sb, "</label>\n"); 552 if (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTME) 553 sbuf_printf(sb, "%s<attrib>bootme</attrib>\n", indent); 554 if (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTONCE) { 555 sbuf_printf(sb, "%s<attrib>bootonce</attrib>\n", 556 indent); 557 } 558 if (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTFAILED) { 559 sbuf_printf(sb, "%s<attrib>bootfailed</attrib>\n", 560 indent); 561 } 562 sbuf_printf(sb, "%s<rawtype>", indent); 563 sbuf_printf_uuid(sb, &entry->ent.ent_type); 564 sbuf_printf(sb, "</rawtype>\n"); 565 sbuf_printf(sb, "%s<rawuuid>", indent); 566 sbuf_printf_uuid(sb, &entry->ent.ent_uuid); 567 sbuf_printf(sb, "</rawuuid>\n"); 568 } else { 569 /* confxml: scheme information */ 570 } 571} 572 573static int 574g_part_gpt_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 575{ 576 struct g_part_gpt_entry *entry; 577 578 entry = (struct g_part_gpt_entry *)baseentry; 579 return ((EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd_swap) || 580 EQUUID(&entry->ent.ent_type, &gpt_uuid_linux_swap)) ? 1 : 0); 581} 582 583static int 584g_part_gpt_modify(struct g_part_table *basetable, 585 struct g_part_entry *baseentry, struct g_part_parms *gpp) 586{ 587 struct g_part_gpt_entry *entry; 588 int error; 589 590 entry = (struct g_part_gpt_entry *)baseentry; 591 if (gpp->gpp_parms & G_PART_PARM_TYPE) { 592 error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type); 593 if (error) 594 return (error); 595 } 596 if (gpp->gpp_parms & G_PART_PARM_LABEL) 597 g_gpt_utf8_to_utf16(gpp->gpp_label, entry->ent.ent_name, 598 sizeof(entry->ent.ent_name) / 599 sizeof(entry->ent.ent_name[0])); 600 return (0); 601} 602 603static int 604g_part_gpt_resize(struct g_part_table *basetable, 605 struct g_part_entry *baseentry, struct g_part_parms *gpp) 606{ 607 struct g_part_gpt_entry *entry; 608 entry = (struct g_part_gpt_entry *)baseentry; 609 610 baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; 611 entry->ent.ent_lba_end = baseentry->gpe_end; 612 613 return (0); 614} 615 616static const char * 617g_part_gpt_name(struct g_part_table *table, struct g_part_entry *baseentry, 618 char *buf, size_t bufsz) 619{ 620 struct g_part_gpt_entry *entry; 621 char c; 622 623 entry = (struct g_part_gpt_entry *)baseentry; 624 c = (EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd)) ? 's' : 'p'; 625 snprintf(buf, bufsz, "%c%d", c, baseentry->gpe_index); 626 return (buf); 627} 628 629static int 630g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp) 631{ 632 struct g_provider *pp; 633 char *buf; 634 int error, res; 635 636 /* We don't nest, which means that our depth should be 0. */ 637 if (table->gpt_depth != 0) 638 return (ENXIO); 639 640 pp = cp->provider; 641 642 /* 643 * Sanity-check the provider. Since the first sector on the provider 644 * must be a PMBR and a PMBR is 512 bytes large, the sector size 645 * must be at least 512 bytes. Also, since the theoretical minimum 646 * number of sectors needed by GPT is 6, any medium that has less 647 * than 6 sectors is never going to be able to hold a GPT. The 648 * number 6 comes from: 649 * 1 sector for the PMBR 650 * 2 sectors for the GPT headers (each 1 sector) 651 * 2 sectors for the GPT tables (each 1 sector) 652 * 1 sector for an actual partition 653 * It's better to catch this pathological case early than behaving 654 * pathologically later on... 655 */ 656 if (pp->sectorsize < MBRSIZE || pp->mediasize < 6 * pp->sectorsize) 657 return (ENOSPC); 658 659 /* Check that there's a MBR. */ 660 buf = g_read_data(cp, 0L, pp->sectorsize, &error); 661 if (buf == NULL) 662 return (error); 663 res = le16dec(buf + DOSMAGICOFFSET); 664 g_free(buf); 665 if (res != DOSMAGIC) 666 return (ENXIO); 667 668 /* Check that there's a primary header. */ 669 buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); 670 if (buf == NULL) 671 return (error); 672 res = memcmp(buf, GPT_HDR_SIG, 8); 673 g_free(buf); 674 if (res == 0) 675 return (G_PART_PROBE_PRI_HIGH); 676 677 /* No primary? Check that there's a secondary. */ 678 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 679 &error); 680 if (buf == NULL) 681 return (error); 682 res = memcmp(buf, GPT_HDR_SIG, 8); 683 g_free(buf); 684 return ((res == 0) ? G_PART_PROBE_PRI_HIGH : ENXIO); 685} 686 687static int 688g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp) 689{ 690 struct gpt_hdr *prihdr, *sechdr; 691 struct gpt_ent *tbl, *pritbl, *sectbl; 692 struct g_provider *pp; 693 struct g_part_gpt_table *table; 694 struct g_part_gpt_entry *entry; 695 u_char *buf; 696 uint64_t last; 697 int error, index; 698 699 table = (struct g_part_gpt_table *)basetable; 700 pp = cp->provider; 701 last = (pp->mediasize / pp->sectorsize) - 1; 702 703 /* Read the PMBR */ 704 buf = g_read_data(cp, 0, pp->sectorsize, &error); 705 if (buf == NULL) 706 return (error); 707 bcopy(buf, table->mbr, MBRSIZE); 708 g_free(buf); 709 710 /* Read the primary header and table. */ 711 prihdr = gpt_read_hdr(table, cp, GPT_ELT_PRIHDR); 712 if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) { 713 pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, prihdr); 714 } else { 715 table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 716 pritbl = NULL; 717 } 718 719 /* Read the secondary header and table. */ 720 sechdr = gpt_read_hdr(table, cp, GPT_ELT_SECHDR); 721 if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) { 722 sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, sechdr); 723 } else { 724 table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 725 sectbl = NULL; 726 } 727 728 /* Fail if we haven't got any good tables at all. */ 729 if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK && 730 table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 731 printf("GEOM: %s: corrupt or invalid GPT detected.\n", 732 pp->name); 733 printf("GEOM: %s: GPT rejected -- may not be recoverable.\n", 734 pp->name); 735 return (EINVAL); 736 } 737 738 /* 739 * If both headers are good but they disagree with each other, 740 * then invalidate one. We prefer to keep the primary header, 741 * unless the primary table is corrupt. 742 */ 743 if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK && 744 table->state[GPT_ELT_SECHDR] == GPT_STATE_OK && 745 !gpt_matched_hdrs(prihdr, sechdr)) { 746 if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK) { 747 table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID; 748 table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING; 749 g_free(sechdr); 750 sechdr = NULL; 751 } else { 752 table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID; 753 table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING; 754 g_free(prihdr); 755 prihdr = NULL; 756 } 757 } 758 759 if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK) { 760 printf("GEOM: %s: the primary GPT table is corrupt or " 761 "invalid.\n", pp->name); 762 printf("GEOM: %s: using the secondary instead -- recovery " 763 "strongly advised.\n", pp->name); 764 table->hdr = sechdr; 765 basetable->gpt_corrupt = 1; 766 if (prihdr != NULL) 767 g_free(prihdr); 768 tbl = sectbl; 769 if (pritbl != NULL) 770 g_free(pritbl); 771 } else { 772 if (table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) { 773 printf("GEOM: %s: the secondary GPT table is corrupt " 774 "or invalid.\n", pp->name); 775 printf("GEOM: %s: using the primary only -- recovery " 776 "suggested.\n", pp->name); 777 basetable->gpt_corrupt = 1; 778 } else if (table->lba[GPT_ELT_SECHDR] != last) { 779 printf( "GEOM: %s: the secondary GPT header is not in " 780 "the last LBA.\n", pp->name); 781 basetable->gpt_corrupt = 1; 782 } 783 table->hdr = prihdr; 784 if (sechdr != NULL) 785 g_free(sechdr); 786 tbl = pritbl; 787 if (sectbl != NULL) 788 g_free(sectbl); 789 } 790 791 basetable->gpt_first = table->hdr->hdr_lba_start; 792 basetable->gpt_last = table->hdr->hdr_lba_end; 793 basetable->gpt_entries = table->hdr->hdr_entries; 794 795 for (index = basetable->gpt_entries - 1; index >= 0; index--) { 796 if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused)) 797 continue; 798 entry = (struct g_part_gpt_entry *)g_part_new_entry( 799 basetable, index + 1, tbl[index].ent_lba_start, 800 tbl[index].ent_lba_end); 801 entry->ent = tbl[index]; 802 } 803 804 g_free(tbl); 805 return (0); 806} 807 808static int 809g_part_gpt_recover(struct g_part_table *basetable) 810{ 811 812 g_gpt_set_defaults(basetable, 813 LIST_FIRST(&basetable->gpt_gp->consumer)->provider); 814 basetable->gpt_corrupt = 0; 815 return (0); 816} 817 818static int 819g_part_gpt_setunset(struct g_part_table *table, struct g_part_entry *baseentry, 820 const char *attrib, unsigned int set) 821{ 822 struct g_part_entry *iter; 823 struct g_part_gpt_entry *entry; 824 int changed, bootme, bootonce, bootfailed; 825 826 bootme = bootonce = bootfailed = 0; 827 if (strcasecmp(attrib, "bootme") == 0) { 828 bootme = 1; 829 } else if (strcasecmp(attrib, "bootonce") == 0) { 830 /* BOOTME is set automatically with BOOTONCE, but not unset. */ 831 bootonce = 1; 832 if (set) 833 bootme = 1; 834 } else if (strcasecmp(attrib, "bootfailed") == 0) { 835 /* 836 * It should only be possible to unset BOOTFAILED, but it might 837 * be useful for test purposes to also be able to set it. 838 */ 839 bootfailed = 1; 840 } 841 if (!bootme && !bootonce && !bootfailed) 842 return (EINVAL); 843 844 LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) { 845 if (iter->gpe_deleted) 846 continue; 847 if (iter != baseentry) 848 continue; 849 changed = 0; 850 entry = (struct g_part_gpt_entry *)iter; 851 if (set) { 852 if (bootme && 853 !(entry->ent.ent_attr & GPT_ENT_ATTR_BOOTME)) { 854 entry->ent.ent_attr |= GPT_ENT_ATTR_BOOTME; 855 changed = 1; 856 } 857 if (bootonce && 858 !(entry->ent.ent_attr & GPT_ENT_ATTR_BOOTONCE)) { 859 entry->ent.ent_attr |= GPT_ENT_ATTR_BOOTONCE; 860 changed = 1; 861 } 862 if (bootfailed && 863 !(entry->ent.ent_attr & GPT_ENT_ATTR_BOOTFAILED)) { 864 entry->ent.ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 865 changed = 1; 866 } 867 } else { 868 if (bootme && 869 (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTME)) { 870 entry->ent.ent_attr &= ~GPT_ENT_ATTR_BOOTME; 871 changed = 1; 872 } 873 if (bootonce && 874 (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTONCE)) { 875 entry->ent.ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 876 changed = 1; 877 } 878 if (bootfailed && 879 (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTFAILED)) { 880 entry->ent.ent_attr &= ~GPT_ENT_ATTR_BOOTFAILED; 881 changed = 1; 882 } 883 } 884 if (changed && !iter->gpe_created) 885 iter->gpe_modified = 1; 886 } 887 return (0); 888} 889 890static const char * 891g_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 892 char *buf, size_t bufsz) 893{ 894 struct g_part_gpt_entry *entry; 895 struct uuid *type; 896 struct g_part_uuid_alias *uap; 897 898 entry = (struct g_part_gpt_entry *)baseentry; 899 type = &entry->ent.ent_type; 900 for (uap = &gpt_uuid_alias_match[0]; uap->uuid; uap++) 901 if (EQUUID(type, uap->uuid)) 902 return (g_part_alias_name(uap->alias)); 903 buf[0] = '!'; 904 snprintf_uuid(buf + 1, bufsz - 1, type); 905 906 return (buf); 907} 908 909static int 910g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp) 911{ 912 unsigned char *buf, *bp; 913 struct g_provider *pp; 914 struct g_part_entry *baseentry; 915 struct g_part_gpt_entry *entry; 916 struct g_part_gpt_table *table; 917 size_t tblsz; 918 uint32_t crc; 919 int error, index; 920 921 pp = cp->provider; 922 table = (struct g_part_gpt_table *)basetable; 923 tblsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz + 924 pp->sectorsize - 1) / pp->sectorsize; 925 926 /* Write the PMBR */ 927 buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 928 bcopy(table->mbr, buf, MBRSIZE); 929 error = g_write_data(cp, 0, buf, pp->sectorsize); 930 g_free(buf); 931 if (error) 932 return (error); 933 934 /* Allocate space for the header and entries. */ 935 buf = g_malloc((tblsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO); 936 937 memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); 938 le32enc(buf + 8, table->hdr->hdr_revision); 939 le32enc(buf + 12, table->hdr->hdr_size); 940 le64enc(buf + 40, table->hdr->hdr_lba_start); 941 le64enc(buf + 48, table->hdr->hdr_lba_end); 942 le_uuid_enc(buf + 56, &table->hdr->hdr_uuid); 943 le32enc(buf + 80, table->hdr->hdr_entries); 944 le32enc(buf + 84, table->hdr->hdr_entsz); 945 946 LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { 947 if (baseentry->gpe_deleted) 948 continue; 949 entry = (struct g_part_gpt_entry *)baseentry; 950 index = baseentry->gpe_index - 1; 951 bp = buf + pp->sectorsize + table->hdr->hdr_entsz * index; 952 le_uuid_enc(bp, &entry->ent.ent_type); 953 le_uuid_enc(bp + 16, &entry->ent.ent_uuid); 954 le64enc(bp + 32, entry->ent.ent_lba_start); 955 le64enc(bp + 40, entry->ent.ent_lba_end); 956 le64enc(bp + 48, entry->ent.ent_attr); 957 memcpy(bp + 56, entry->ent.ent_name, 958 sizeof(entry->ent.ent_name)); 959 } 960 961 crc = crc32(buf + pp->sectorsize, 962 table->hdr->hdr_entries * table->hdr->hdr_entsz); 963 le32enc(buf + 88, crc); 964 965 /* Write primary meta-data. */ 966 le32enc(buf + 16, 0); /* hdr_crc_self. */ 967 le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_self. */ 968 le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_alt. */ 969 le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]); /* hdr_lba_table. */ 970 crc = crc32(buf, table->hdr->hdr_size); 971 le32enc(buf + 16, crc); 972 973 for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) { 974 error = g_write_data(cp, 975 (table->lba[GPT_ELT_PRITBL] + index) * pp->sectorsize, 976 buf + (index + 1) * pp->sectorsize, 977 (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS: 978 (tblsz - index) * pp->sectorsize); 979 if (error) 980 goto out; 981 } 982 error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize, 983 buf, pp->sectorsize); 984 if (error) 985 goto out; 986 987 /* Write secondary meta-data. */ 988 le32enc(buf + 16, 0); /* hdr_crc_self. */ 989 le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_self. */ 990 le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_alt. */ 991 le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]); /* hdr_lba_table. */ 992 crc = crc32(buf, table->hdr->hdr_size); 993 le32enc(buf + 16, crc); 994 995 for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) { 996 error = g_write_data(cp, 997 (table->lba[GPT_ELT_SECTBL] + index) * pp->sectorsize, 998 buf + (index + 1) * pp->sectorsize, 999 (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS: 1000 (tblsz - index) * pp->sectorsize); 1001 if (error) 1002 goto out; 1003 } 1004 error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize, 1005 buf, pp->sectorsize); 1006 1007 out: 1008 g_free(buf); 1009 return (error); 1010} 1011 1012static void 1013g_gpt_set_defaults(struct g_part_table *basetable, struct g_provider *pp) 1014{ 1015 struct g_part_gpt_table *table; 1016 quad_t last; 1017 size_t tblsz; 1018 1019 table = (struct g_part_gpt_table *)basetable; 1020 last = pp->mediasize / pp->sectorsize - 1; 1021 tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) + 1022 pp->sectorsize - 1) / pp->sectorsize; 1023 1024 table->lba[GPT_ELT_PRIHDR] = 1; 1025 table->lba[GPT_ELT_PRITBL] = 2; 1026 table->lba[GPT_ELT_SECHDR] = last; 1027 table->lba[GPT_ELT_SECTBL] = last - tblsz; 1028 table->state[GPT_ELT_PRIHDR] = GPT_STATE_OK; 1029 table->state[GPT_ELT_PRITBL] = GPT_STATE_OK; 1030 table->state[GPT_ELT_SECHDR] = GPT_STATE_OK; 1031 table->state[GPT_ELT_SECTBL] = GPT_STATE_OK; 1032 1033 table->hdr->hdr_lba_start = 2 + tblsz; 1034 table->hdr->hdr_lba_end = last - tblsz - 1; 1035 1036 basetable->gpt_first = table->hdr->hdr_lba_start; 1037 basetable->gpt_last = table->hdr->hdr_lba_end; 1038} 1039 1040static void 1041g_gpt_printf_utf16(struct sbuf *sb, uint16_t *str, size_t len) 1042{ 1043 u_int bo; 1044 uint32_t ch; 1045 uint16_t c; 1046 1047 bo = LITTLE_ENDIAN; /* GPT is little-endian */ 1048 while (len > 0 && *str != 0) { 1049 ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str); 1050 str++, len--; 1051 if ((ch & 0xf800) == 0xd800) { 1052 if (len > 0) { 1053 c = (bo == BIG_ENDIAN) ? be16toh(*str) 1054 : le16toh(*str); 1055 str++, len--; 1056 } else 1057 c = 0xfffd; 1058 if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) { 1059 ch = ((ch & 0x3ff) << 10) + (c & 0x3ff); 1060 ch += 0x10000; 1061 } else 1062 ch = 0xfffd; 1063 } else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */ 1064 bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN; 1065 continue; 1066 } else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */ 1067 continue; 1068 1069 /* Write the Unicode character in UTF-8 */ 1070 if (ch < 0x80) 1071 sbuf_printf(sb, "%c", ch); 1072 else if (ch < 0x800) 1073 sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6), 1074 0x80 | (ch & 0x3f)); 1075 else if (ch < 0x10000) 1076 sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12), 1077 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 1078 else if (ch < 0x200000) 1079 sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18), 1080 0x80 | ((ch >> 12) & 0x3f), 1081 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); 1082 } 1083} 1084 1085static void 1086g_gpt_utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) 1087{ 1088 size_t s16idx, s8idx; 1089 uint32_t utfchar; 1090 unsigned int c, utfbytes; 1091 1092 s8idx = s16idx = 0; 1093 utfchar = 0; 1094 utfbytes = 0; 1095 bzero(s16, s16len << 1); 1096 while (s8[s8idx] != 0 && s16idx < s16len) { 1097 c = s8[s8idx++]; 1098 if ((c & 0xc0) != 0x80) { 1099 /* Initial characters. */ 1100 if (utfbytes != 0) { 1101 /* Incomplete encoding of previous char. */ 1102 s16[s16idx++] = htole16(0xfffd); 1103 } 1104 if ((c & 0xf8) == 0xf0) { 1105 utfchar = c & 0x07; 1106 utfbytes = 3; 1107 } else if ((c & 0xf0) == 0xe0) { 1108 utfchar = c & 0x0f; 1109 utfbytes = 2; 1110 } else if ((c & 0xe0) == 0xc0) { 1111 utfchar = c & 0x1f; 1112 utfbytes = 1; 1113 } else { 1114 utfchar = c & 0x7f; 1115 utfbytes = 0; 1116 } 1117 } else { 1118 /* Followup characters. */ 1119 if (utfbytes > 0) { 1120 utfchar = (utfchar << 6) + (c & 0x3f); 1121 utfbytes--; 1122 } else if (utfbytes == 0) 1123 utfbytes = ~0; 1124 } 1125 /* 1126 * Write the complete Unicode character as UTF-16 when we 1127 * have all the UTF-8 charactars collected. 1128 */ 1129 if (utfbytes == 0) { 1130 /* 1131 * If we need to write 2 UTF-16 characters, but 1132 * we only have room for 1, then we truncate the 1133 * string by writing a 0 instead. 1134 */ 1135 if (utfchar >= 0x10000 && s16idx < s16len - 1) { 1136 s16[s16idx++] = 1137 htole16(0xd800 | ((utfchar >> 10) - 0x40)); 1138 s16[s16idx++] = 1139 htole16(0xdc00 | (utfchar & 0x3ff)); 1140 } else 1141 s16[s16idx++] = (utfchar >= 0x10000) ? 0 : 1142 htole16(utfchar); 1143 } 1144 } 1145 /* 1146 * If our input string was truncated, append an invalid encoding 1147 * character to the output string. 1148 */ 1149 if (utfbytes != 0 && s16idx < s16len) 1150 s16[s16idx++] = htole16(0xfffd); 1151} 1152