g_part_bsd.c revision 190463
1/*-
2 * Copyright (c) 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_bsd.c 190463 2009-03-27 05:48:42Z marcel $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/disklabel.h>
33#include <sys/endian.h>
34#include <sys/kernel.h>
35#include <sys/kobj.h>
36#include <sys/limits.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/mutex.h>
40#include <sys/queue.h>
41#include <sys/sbuf.h>
42#include <sys/systm.h>
43#include <geom/geom.h>
44#include <geom/part/g_part.h>
45
46#include "g_part_if.h"
47
48struct g_part_bsd_table {
49	struct g_part_table	base;
50	u_char			*bbarea;
51	uint32_t		offset;
52};
53
54struct g_part_bsd_entry {
55	struct g_part_entry	base;
56	struct partition	part;
57};
58
59static int g_part_bsd_add(struct g_part_table *, struct g_part_entry *,
60    struct g_part_parms *);
61static int g_part_bsd_bootcode(struct g_part_table *, struct g_part_parms *);
62static int g_part_bsd_create(struct g_part_table *, struct g_part_parms *);
63static int g_part_bsd_destroy(struct g_part_table *, struct g_part_parms *);
64static void g_part_bsd_dumpconf(struct g_part_table *, struct g_part_entry *,
65    struct sbuf *, const char *);
66static int g_part_bsd_dumpto(struct g_part_table *, struct g_part_entry *);
67static int g_part_bsd_modify(struct g_part_table *, struct g_part_entry *,
68    struct g_part_parms *);
69static const char *g_part_bsd_name(struct g_part_table *, struct g_part_entry *,
70    char *, size_t);
71static int g_part_bsd_probe(struct g_part_table *, struct g_consumer *);
72static int g_part_bsd_read(struct g_part_table *, struct g_consumer *);
73static const char *g_part_bsd_type(struct g_part_table *, struct g_part_entry *,
74    char *, size_t);
75static int g_part_bsd_write(struct g_part_table *, struct g_consumer *);
76
77static kobj_method_t g_part_bsd_methods[] = {
78	KOBJMETHOD(g_part_add,		g_part_bsd_add),
79	KOBJMETHOD(g_part_bootcode,	g_part_bsd_bootcode),
80	KOBJMETHOD(g_part_create,	g_part_bsd_create),
81	KOBJMETHOD(g_part_destroy,	g_part_bsd_destroy),
82	KOBJMETHOD(g_part_dumpconf,	g_part_bsd_dumpconf),
83	KOBJMETHOD(g_part_dumpto,	g_part_bsd_dumpto),
84	KOBJMETHOD(g_part_modify,	g_part_bsd_modify),
85	KOBJMETHOD(g_part_name,		g_part_bsd_name),
86	KOBJMETHOD(g_part_probe,	g_part_bsd_probe),
87	KOBJMETHOD(g_part_read,		g_part_bsd_read),
88	KOBJMETHOD(g_part_type,		g_part_bsd_type),
89	KOBJMETHOD(g_part_write,	g_part_bsd_write),
90	{ 0, 0 }
91};
92
93static struct g_part_scheme g_part_bsd_scheme = {
94	"BSD",
95	g_part_bsd_methods,
96	sizeof(struct g_part_bsd_table),
97	.gps_entrysz = sizeof(struct g_part_bsd_entry),
98	.gps_minent = 8,
99	.gps_maxent = 20,
100	.gps_bootcodesz = BBSIZE,
101};
102G_PART_SCHEME_DECLARE(g_part_bsd);
103
104static int
105bsd_parse_type(const char *type, uint8_t *fstype)
106{
107	const char *alias;
108	char *endp;
109	long lt;
110
111	if (type[0] == '!') {
112		lt = strtol(type + 1, &endp, 0);
113		if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256)
114			return (EINVAL);
115		*fstype = (u_int)lt;
116		return (0);
117	}
118	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
119	if (!strcasecmp(type, alias)) {
120		*fstype = FS_SWAP;
121		return (0);
122	}
123	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
124	if (!strcasecmp(type, alias)) {
125		*fstype = FS_BSDFFS;
126		return (0);
127	}
128	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
129	if (!strcasecmp(type, alias)) {
130		*fstype = FS_VINUM;
131		return (0);
132	}
133	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS);
134	if (!strcasecmp(type, alias)) {
135		*fstype = FS_ZFS;
136		return (0);
137	}
138	return (EINVAL);
139}
140
141static int
142g_part_bsd_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
143    struct g_part_parms *gpp)
144{
145	struct g_part_bsd_entry *entry;
146	struct g_part_bsd_table *table;
147
148	if (gpp->gpp_parms & G_PART_PARM_LABEL)
149		return (EINVAL);
150
151	entry = (struct g_part_bsd_entry *)baseentry;
152	table = (struct g_part_bsd_table *)basetable;
153
154	entry->part.p_size = gpp->gpp_size;
155	entry->part.p_offset = gpp->gpp_start + table->offset;
156	entry->part.p_fsize = 0;
157	entry->part.p_frag = 0;
158	entry->part.p_cpg = 0;
159	return (bsd_parse_type(gpp->gpp_type, &entry->part.p_fstype));
160}
161
162static int
163g_part_bsd_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp)
164{
165	struct g_part_bsd_table *table;
166	const u_char *codeptr;
167	size_t hdsz, tlsz;
168	size_t codesz, tlofs;
169
170	hdsz = 512;
171	tlofs = hdsz + 148 + basetable->gpt_entries * 16;
172	tlsz = BBSIZE - tlofs;
173	table = (struct g_part_bsd_table *)basetable;
174	bzero(table->bbarea, hdsz);
175	bzero(table->bbarea + tlofs, tlsz);
176	codeptr = gpp->gpp_codeptr;
177	codesz = MIN(hdsz, gpp->gpp_codesize);
178	if (codesz > 0)
179		bcopy(codeptr, table->bbarea, codesz);
180	codesz = MIN(tlsz, gpp->gpp_codesize - tlofs);
181	if (codesz > 0)
182		bcopy(codeptr + tlofs, table->bbarea + tlofs, codesz);
183	return (0);
184}
185
186static int
187g_part_bsd_create(struct g_part_table *basetable, struct g_part_parms *gpp)
188{
189	struct g_consumer *cp;
190	struct g_provider *pp;
191	struct g_part_entry *baseentry;
192	struct g_part_bsd_entry *entry;
193	struct g_part_bsd_table *table;
194	u_char *ptr;
195	uint32_t msize, ncyls, secpercyl;
196
197	pp = gpp->gpp_provider;
198	cp = LIST_FIRST(&pp->consumers);
199
200	if (pp->sectorsize < sizeof(struct disklabel))
201		return (ENOSPC);
202	if (BBSIZE % pp->sectorsize)
203		return (ENOTBLK);
204
205	msize = MIN(pp->mediasize / pp->sectorsize, 0xffffffff);
206	secpercyl = basetable->gpt_sectors * basetable->gpt_heads;
207	ncyls = msize / secpercyl;
208
209	table = (struct g_part_bsd_table *)basetable;
210	table->bbarea = g_malloc(BBSIZE, M_WAITOK | M_ZERO);
211	ptr = table->bbarea + pp->sectorsize;
212
213	le32enc(ptr + 0, DISKMAGIC);			/* d_magic */
214	le32enc(ptr + 40, pp->sectorsize);		/* d_secsize */
215	le32enc(ptr + 44, basetable->gpt_sectors);	/* d_nsectors */
216	le32enc(ptr + 48, basetable->gpt_heads);	/* d_ntracks */
217	le32enc(ptr + 52, ncyls);			/* d_ncylinders */
218	le32enc(ptr + 56, secpercyl);			/* d_secpercyl */
219	le32enc(ptr + 60, msize);			/* d_secperunit */
220	le16enc(ptr + 72, 3600);			/* d_rpm */
221	le32enc(ptr + 132, DISKMAGIC);			/* d_magic2 */
222	le16enc(ptr + 138, basetable->gpt_entries);	/* d_npartitions */
223	le32enc(ptr + 140, BBSIZE);			/* d_bbsize */
224
225	basetable->gpt_first = 0;
226	basetable->gpt_last = msize - 1;
227	basetable->gpt_isleaf = 1;
228
229	baseentry = g_part_new_entry(basetable, RAW_PART + 1,
230	    basetable->gpt_first, basetable->gpt_last);
231	baseentry->gpe_internal = 1;
232	entry = (struct g_part_bsd_entry *)baseentry;
233	entry->part.p_size = basetable->gpt_last + 1;
234	entry->part.p_offset = table->offset;
235
236	return (0);
237}
238
239static int
240g_part_bsd_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
241{
242
243	/* Wipe the second sector to clear the partitioning. */
244	basetable->gpt_smhead |= 2;
245	return (0);
246}
247
248static void
249g_part_bsd_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry,
250    struct sbuf *sb, const char *indent)
251{
252	struct g_part_bsd_entry *entry;
253
254	entry = (struct g_part_bsd_entry *)baseentry;
255	if (indent == NULL) {
256		/* conftxt: libdisk compatibility */
257		sbuf_printf(sb, " xs BSD xt %u", entry->part.p_fstype);
258	} else if (entry != NULL) {
259		/* confxml: partition entry information */
260		sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
261		    entry->part.p_fstype);
262	} else {
263		/* confxml: scheme information */
264	}
265}
266
267static int
268g_part_bsd_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)
269{
270	struct g_part_bsd_entry *entry;
271
272	/* Allow dumping to a swap partition or an unused partition. */
273	entry = (struct g_part_bsd_entry *)baseentry;
274	return ((entry->part.p_fstype == FS_UNUSED ||
275	    entry->part.p_fstype == FS_SWAP) ? 1 : 0);
276}
277
278static int
279g_part_bsd_modify(struct g_part_table *basetable,
280    struct g_part_entry *baseentry, struct g_part_parms *gpp)
281{
282	struct g_part_bsd_entry *entry;
283
284	if (gpp->gpp_parms & G_PART_PARM_LABEL)
285		return (EINVAL);
286
287	entry = (struct g_part_bsd_entry *)baseentry;
288	if (gpp->gpp_parms & G_PART_PARM_TYPE)
289		return (bsd_parse_type(gpp->gpp_type, &entry->part.p_fstype));
290	return (0);
291}
292
293static const char *
294g_part_bsd_name(struct g_part_table *table, struct g_part_entry *baseentry,
295    char *buf, size_t bufsz)
296{
297
298	snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
299	return (buf);
300}
301
302static int
303g_part_bsd_probe(struct g_part_table *table, struct g_consumer *cp)
304{
305	struct g_provider *pp;
306	u_char *buf;
307	uint32_t magic1, magic2;
308	int error;
309
310	pp = cp->provider;
311
312	/* Sanity-check the provider. */
313	if (pp->sectorsize < sizeof(struct disklabel) ||
314	    pp->mediasize < BBSIZE)
315		return (ENOSPC);
316	if (BBSIZE % pp->sectorsize)
317		return (ENOTBLK);
318
319	/* Check that there's a disklabel. */
320	buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
321	if (buf == NULL)
322		return (error);
323	magic1 = le32dec(buf + 0);
324	magic2 = le32dec(buf + 132);
325	g_free(buf);
326	return ((magic1 == DISKMAGIC && magic2 == DISKMAGIC)
327	    ? G_PART_PROBE_PRI_HIGH : ENXIO);
328}
329
330static int
331g_part_bsd_read(struct g_part_table *basetable, struct g_consumer *cp)
332{
333	struct g_provider *pp;
334	struct g_part_bsd_table *table;
335	struct g_part_entry *baseentry;
336	struct g_part_bsd_entry *entry;
337	struct partition part;
338	u_char *buf, *p;
339	off_t chs, msize;
340	u_int sectors, heads;
341	int error, index;
342
343	pp = cp->provider;
344	table = (struct g_part_bsd_table *)basetable;
345	msize = pp->mediasize / pp->sectorsize;
346
347	table->bbarea = g_read_data(cp, 0, BBSIZE, &error);
348	if (table->bbarea == NULL)
349		return (error);
350
351	buf = table->bbarea + pp->sectorsize;
352
353	if (le32dec(buf + 40) != pp->sectorsize)
354		goto invalid_label;
355	sectors = le32dec(buf + 44);
356	if (sectors < 1 || sectors > 255)
357		goto invalid_label;
358	if (sectors != basetable->gpt_sectors && !basetable->gpt_fixgeom) {
359		g_part_geometry_heads(msize, sectors, &chs, &heads);
360		if (chs != 0) {
361			basetable->gpt_sectors = sectors;
362			basetable->gpt_heads = heads;
363		}
364	}
365	heads = le32dec(buf + 48);
366	if (heads < 1 || heads > 255)
367		goto invalid_label;
368	if (heads != basetable->gpt_heads && !basetable->gpt_fixgeom)
369		basetable->gpt_heads = heads;
370	if (sectors != basetable->gpt_sectors || heads != basetable->gpt_heads)
371		printf("GEOM: %s: geometry does not match label"
372		    " (%uh,%us != %uh,%us).\n", pp->name, heads, sectors,
373		    basetable->gpt_heads, basetable->gpt_sectors);
374
375	chs = le32dec(buf + 60);
376	if (chs < 1)
377		goto invalid_label;
378	/* Fix-up a sysinstall bug. */
379	if (chs > msize) {
380		chs = msize;
381		le32enc(buf + 60, msize);
382	}
383	if (chs != msize)
384		printf("GEOM: %s: media size does not match label.\n",
385		    pp->name);
386
387	basetable->gpt_first = 0;
388	basetable->gpt_last = msize - 1;
389	basetable->gpt_isleaf = 1;
390
391	basetable->gpt_entries = le16dec(buf + 138);
392	if (basetable->gpt_entries < g_part_bsd_scheme.gps_minent ||
393	    basetable->gpt_entries > g_part_bsd_scheme.gps_maxent)
394		goto invalid_label;
395
396	table->offset = le32dec(buf + 148 + RAW_PART * 16 + 4);
397	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
398		p = buf + 148 + index * 16;
399		part.p_size = le32dec(p + 0);
400		part.p_offset = le32dec(p + 4);
401		part.p_fsize = le32dec(p + 8);
402		part.p_fstype = p[12];
403		part.p_frag = p[13];
404		part.p_cpg = le16dec(p + 14);
405		if (part.p_size == 0)
406			continue;
407		if (part.p_offset < table->offset)
408			continue;
409		baseentry = g_part_new_entry(basetable, index + 1,
410		    part.p_offset - table->offset,
411		    part.p_offset - table->offset + part.p_size - 1);
412		entry = (struct g_part_bsd_entry *)baseentry;
413		entry->part = part;
414		if (index == RAW_PART)
415			baseentry->gpe_internal = 1;
416	}
417
418	return (0);
419
420 invalid_label:
421	printf("GEOM: %s: invalid disklabel.\n", pp->name);
422	g_free(table->bbarea);
423	return (EINVAL);
424}
425
426static const char *
427g_part_bsd_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
428    char *buf, size_t bufsz)
429{
430	struct g_part_bsd_entry *entry;
431	int type;
432
433	entry = (struct g_part_bsd_entry *)baseentry;
434	type = entry->part.p_fstype;
435	if (type == FS_SWAP)
436		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
437	if (type == FS_BSDFFS)
438		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
439	if (type == FS_VINUM)
440		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
441	if (type == FS_ZFS)
442		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS));
443	snprintf(buf, bufsz, "!%d", type);
444	return (buf);
445}
446
447static int
448g_part_bsd_write(struct g_part_table *basetable, struct g_consumer *cp)
449{
450	struct g_provider *pp;
451	struct g_part_entry *baseentry;
452	struct g_part_bsd_entry *entry;
453	struct g_part_bsd_table *table;
454	uint16_t sum;
455	u_char *label, *p, *pe;
456	int error, index;
457
458	pp = cp->provider;
459	table = (struct g_part_bsd_table *)basetable;
460	baseentry = LIST_FIRST(&basetable->gpt_entry);
461	label = table->bbarea + pp->sectorsize;
462	for (index = 1; index <= basetable->gpt_entries; index++) {
463		p = label + 148 + (index - 1) * 16;
464		entry = (baseentry != NULL && index == baseentry->gpe_index)
465		    ? (struct g_part_bsd_entry *)baseentry : NULL;
466		if (entry != NULL && !baseentry->gpe_deleted) {
467			le32enc(p + 0, entry->part.p_size);
468			le32enc(p + 4, entry->part.p_offset);
469			le32enc(p + 8, entry->part.p_fsize);
470			p[12] = entry->part.p_fstype;
471			p[13] = entry->part.p_frag;
472			le16enc(p + 14, entry->part.p_cpg);
473		} else
474			bzero(p, 16);
475
476		if (entry != NULL)
477			baseentry = LIST_NEXT(baseentry, gpe_entry);
478	}
479
480	/* Calculate checksum. */
481	le16enc(label + 136, 0);
482	pe = label + 148 + basetable->gpt_entries * 16;
483	sum = 0;
484	for (p = label; p < pe; p += 2)
485		sum ^= le16dec(p);
486	le16enc(label + 136, sum);
487
488	error = g_write_data(cp, 0, table->bbarea, BBSIZE);
489	return (error);
490}
491