1/*-
2 * Copyright (c) 2008 Marius Nuennerich
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/kobj.h>
35#include <sys/gpt.h>
36#include <sys/sbuf.h>
37
38#include <geom/geom.h>
39#include <geom/label/g_label.h>
40#include <geom/part/g_part.h>
41
42#define	PART_CLASS_NAME	"PART"
43#define	SCHEME_NAME	"GPT"
44
45#define	G_LABEL_GPT_VOLUME_DIR	"gpt"
46#define	G_LABEL_GPT_ID_DIR	"gptid"
47
48/* XXX: Also defined in geom/part/g_part_gpt.c */
49struct g_part_gpt_entry {
50	struct g_part_entry     base;
51	struct gpt_ent          ent;
52};
53
54/* XXX: Shamelessly stolen from g_part_gpt.c */
55static void
56sbuf_nprintf_utf16(struct sbuf *sb, uint16_t *str, size_t len)
57{
58	u_int bo;
59	uint32_t ch;
60	uint16_t c;
61
62	bo = LITTLE_ENDIAN;	/* GPT is little-endian */
63	while (len > 0 && *str != 0) {
64		ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str);
65		str++, len--;
66		if ((ch & 0xf800) == 0xd800) {
67			if (len > 0) {
68				c = (bo == BIG_ENDIAN) ? be16toh(*str)
69				    : le16toh(*str);
70				str++, len--;
71			} else
72				c = 0xfffd;
73			if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) {
74				ch = ((ch & 0x3ff) << 10) + (c & 0x3ff);
75				ch += 0x10000;
76			} else
77				ch = 0xfffd;
78		} else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */
79			bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN;
80			continue;
81		} else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */
82			continue;
83
84		/* Write the Unicode character in UTF-8 */
85		if (ch < 0x80)
86			sbuf_printf(sb, "%c", ch);
87		else if (ch < 0x800)
88			sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6),
89			    0x80 | (ch & 0x3f));
90		else if (ch < 0x10000)
91			sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12),
92			    0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f));
93		else if (ch < 0x200000)
94			sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18),
95			    0x80 | ((ch >> 12) & 0x3f),
96			    0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f));
97	}
98}
99
100static void
101g_label_gpt_taste(struct g_consumer *cp, char *label, size_t size)
102{
103	struct g_provider *pp;
104	struct g_part_table *tp;
105	struct g_part_gpt_entry *part_gpt_entry;
106	struct sbuf *lbl;
107
108	g_topology_assert_not();
109	pp = cp->provider;
110	tp = (struct g_part_table *)pp->geom->softc;
111	label[0] = '\0';
112
113	/* We taste only partitions handled by GPART */
114	if (strncmp(pp->geom->class->name, PART_CLASS_NAME, sizeof(PART_CLASS_NAME)))
115		return;
116	/* and only GPT */
117	if (strncmp(tp->gpt_scheme->name, SCHEME_NAME, sizeof(SCHEME_NAME)))
118		return;
119
120	part_gpt_entry = (struct g_part_gpt_entry *)pp->private;
121
122	/*
123	 * Create sbuf with biggest possible size.
124	 * We need max. 4 bytes for every 2-byte utf16 char.
125	 */
126	lbl = sbuf_new(NULL, NULL, sizeof(part_gpt_entry->ent.ent_name) << 1, SBUF_FIXEDLEN);
127	/* Size is the number of characters, not bytes */
128	sbuf_nprintf_utf16(lbl, part_gpt_entry->ent.ent_name, sizeof(part_gpt_entry->ent.ent_name) >> 1);
129	sbuf_finish(lbl);
130	strlcpy(label, sbuf_data(lbl), size);
131	sbuf_delete(lbl);
132}
133
134static void
135g_label_gpt_uuid_taste(struct g_consumer *cp, char *label, size_t size)
136{
137	struct g_provider *pp;
138	struct g_part_table *tp;
139	struct g_part_gpt_entry *part_gpt_entry;
140
141	g_topology_assert_not();
142	pp = cp->provider;
143	tp = (struct g_part_table *)pp->geom->softc;
144	label[0] = '\0';
145
146	/* We taste only partitions handled by GPART */
147	if (strncmp(pp->geom->class->name, PART_CLASS_NAME, sizeof(PART_CLASS_NAME)))
148		return;
149	/* and only GPT */
150	if (strncmp(tp->gpt_scheme->name, SCHEME_NAME, sizeof(SCHEME_NAME)))
151		return;
152
153	part_gpt_entry = (struct g_part_gpt_entry *)pp->private;
154	snprintf_uuid(label, size, &part_gpt_entry->ent.ent_uuid);
155}
156
157struct g_label_desc g_label_gpt = {
158	.ld_taste = g_label_gpt_taste,
159	.ld_dir = G_LABEL_GPT_VOLUME_DIR,
160	.ld_enabled = 1
161};
162
163struct g_label_desc g_label_gpt_uuid = {
164	.ld_taste = g_label_gpt_uuid_taste,
165	.ld_dir = G_LABEL_GPT_ID_DIR,
166	.ld_enabled = 1
167};
168
169G_LABEL_INIT(gpt, g_label_gpt, "Create device nodes for GPT labels");
170G_LABEL_INIT(gptid, g_label_gpt_uuid, "Create device nodes for GPT UUIDs");
171