g_part_bsd.c revision 174326
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 174326 2007-12-06 02:32: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			*label;
51};
52
53struct g_part_bsd_entry {
54	struct g_part_entry	base;
55	struct partition	part;
56};
57
58static int g_part_bsd_add(struct g_part_table *, struct g_part_entry *,
59    struct g_part_parms *);
60static int g_part_bsd_create(struct g_part_table *, struct g_part_parms *);
61static int g_part_bsd_destroy(struct g_part_table *, struct g_part_parms *);
62static int g_part_bsd_dumpto(struct g_part_table *, struct g_part_entry *);
63static int g_part_bsd_modify(struct g_part_table *, struct g_part_entry *,
64    struct g_part_parms *);
65static char *g_part_bsd_name(struct g_part_table *, struct g_part_entry *,
66    char *, size_t);
67static int g_part_bsd_probe(struct g_part_table *, struct g_consumer *);
68static int g_part_bsd_read(struct g_part_table *, struct g_consumer *);
69static const char *g_part_bsd_type(struct g_part_table *, struct g_part_entry *,
70    char *, size_t);
71static int g_part_bsd_write(struct g_part_table *, struct g_consumer *);
72
73static kobj_method_t g_part_bsd_methods[] = {
74	KOBJMETHOD(g_part_add,		g_part_bsd_add),
75	KOBJMETHOD(g_part_create,	g_part_bsd_create),
76	KOBJMETHOD(g_part_destroy,	g_part_bsd_destroy),
77	KOBJMETHOD(g_part_dumpto,	g_part_bsd_dumpto),
78	KOBJMETHOD(g_part_modify,	g_part_bsd_modify),
79	KOBJMETHOD(g_part_name,		g_part_bsd_name),
80	KOBJMETHOD(g_part_probe,	g_part_bsd_probe),
81	KOBJMETHOD(g_part_read,		g_part_bsd_read),
82	KOBJMETHOD(g_part_type,		g_part_bsd_type),
83	KOBJMETHOD(g_part_write,	g_part_bsd_write),
84	{ 0, 0 }
85};
86
87static struct g_part_scheme g_part_bsd_scheme = {
88	"BSD",
89	g_part_bsd_methods,
90	sizeof(struct g_part_bsd_table),
91	.gps_entrysz = sizeof(struct g_part_bsd_entry),
92	.gps_minent = 8,
93	.gps_maxent = 20,
94};
95G_PART_SCHEME_DECLARE(g_part_bsd_scheme);
96
97static int
98bsd_parse_type(const char *type, uint8_t *fstype)
99{
100	const char *alias;
101	char *endp;
102	long lt;
103
104	if (type[0] == '!') {
105		lt = strtol(type + 1, &endp, 0);
106		if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256)
107			return (EINVAL);
108		*fstype = (u_int)lt;
109		return (0);
110	}
111	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
112	if (!strcasecmp(type, alias)) {
113		*fstype = FS_SWAP;
114		return (0);
115	}
116	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
117	if (!strcasecmp(type, alias)) {
118		*fstype = FS_BSDFFS;
119		return (0);
120	}
121	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
122	if (!strcasecmp(type, alias)) {
123		*fstype = FS_VINUM;
124		return (0);
125	}
126	return (EINVAL);
127}
128
129static int
130g_part_bsd_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
131    struct g_part_parms *gpp)
132{
133	struct g_part_bsd_entry *entry;
134	struct g_part_bsd_table *table;
135	uint32_t start, size, sectors;
136
137	if (gpp->gpp_parms & G_PART_PARM_LABEL)
138		return (EINVAL);
139
140	sectors = basetable->gpt_sectors;
141
142	entry = (struct g_part_bsd_entry *)baseentry;
143	table = (struct g_part_bsd_table *)basetable;
144
145	start = gpp->gpp_start;
146	size = gpp->gpp_size;
147	if (size < sectors)
148		return (EINVAL);
149	if (start % sectors) {
150		size = size - sectors + (start % sectors);
151		start = start - (start % sectors) + sectors;
152	}
153	if (size % sectors)
154		size = size - (size % sectors);
155	if (size < sectors)
156		return (EINVAL);
157
158	KASSERT(baseentry->gpe_start <= start, (__func__));
159	KASSERT(baseentry->gpe_end >= start + size - 1, (__func__));
160	baseentry->gpe_start = start;
161	baseentry->gpe_end = start + size - 1;
162	entry->part.p_size = size;
163	entry->part.p_offset = start + basetable->gpt_offset;
164	entry->part.p_fsize = 0;
165	entry->part.p_frag = 0;
166	entry->part.p_cpg = 0;
167	return (bsd_parse_type(gpp->gpp_type, &entry->part.p_fstype));
168}
169
170static int
171g_part_bsd_create(struct g_part_table *basetable, struct g_part_parms *gpp)
172{
173	struct g_consumer *cp;
174	struct g_provider *pp;
175	struct g_part_entry *baseentry;
176	struct g_part_bsd_entry *entry;
177	struct g_part_bsd_table *table;
178	u_char *ptr;
179	uint64_t msize;
180	uint32_t ncyls, secpercyl;
181
182	pp = gpp->gpp_provider;
183	cp = LIST_FIRST(&pp->consumers);
184
185	if (pp->sectorsize < sizeof(struct disklabel))
186		return (ENOSPC);
187
188	msize = pp->mediasize / pp->sectorsize;
189	secpercyl = basetable->gpt_sectors * basetable->gpt_heads;
190	ncyls = msize / secpercyl;
191
192	table = (struct g_part_bsd_table *)basetable;
193	ptr = table->label = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
194
195	le32enc(ptr + 0, DISKMAGIC);			/* d_magic */
196	le32enc(ptr + 40, pp->sectorsize);		/* d_secsize */
197	le32enc(ptr + 44, basetable->gpt_sectors);	/* d_nsectors */
198	le32enc(ptr + 48, basetable->gpt_heads);	/* d_ntracks */
199	le32enc(ptr + 52, ncyls);			/* d_ncylinders */
200	le32enc(ptr + 56, secpercyl);			/* d_secpercyl */
201	le32enc(ptr + 60, ncyls * secpercyl);		/* d_secperunit */
202	le16enc(ptr + 72, 3600);			/* d_rpm */
203	le32enc(ptr + 132, DISKMAGIC);			/* d_magic2 */
204	le16enc(ptr + 138, basetable->gpt_entries);	/* d_npartitions */
205	le32enc(ptr + 140, BBSIZE);			/* d_bbsize */
206
207	basetable->gpt_first = 0;
208	basetable->gpt_last = ncyls * secpercyl - 1;
209	basetable->gpt_isleaf = 1;
210
211	baseentry = g_part_new_entry(basetable, RAW_PART + 1,
212	    basetable->gpt_first, basetable->gpt_last);
213	baseentry->gpe_internal = 1;
214	entry = (struct g_part_bsd_entry *)baseentry;
215	entry->part.p_size = basetable->gpt_last + 1;
216	entry->part.p_offset = basetable->gpt_offset;
217
218	return (0);
219}
220
221static int
222g_part_bsd_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
223{
224
225	/* Wipe the second sector to clear the partitioning. */
226	basetable->gpt_smhead |= 2;
227	return (0);
228}
229
230static int
231g_part_bsd_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)
232{
233	struct g_part_bsd_entry *entry;
234
235	/* Allow dumping to a swap partition only. */
236	entry = (struct g_part_bsd_entry *)baseentry;
237	return ((entry->part.p_fstype == FS_SWAP) ? 1 : 0);
238}
239
240static int
241g_part_bsd_modify(struct g_part_table *basetable,
242    struct g_part_entry *baseentry, struct g_part_parms *gpp)
243{
244	struct g_part_bsd_entry *entry;
245
246	if (gpp->gpp_parms & G_PART_PARM_LABEL)
247		return (EINVAL);
248
249	entry = (struct g_part_bsd_entry *)baseentry;
250	if (gpp->gpp_parms & G_PART_PARM_TYPE)
251		return (bsd_parse_type(gpp->gpp_type, &entry->part.p_fstype));
252	return (0);
253}
254
255static char *
256g_part_bsd_name(struct g_part_table *table, struct g_part_entry *baseentry,
257    char *buf, size_t bufsz)
258{
259
260	snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
261	return (buf);
262}
263
264static int
265g_part_bsd_probe(struct g_part_table *table, struct g_consumer *cp)
266{
267	struct g_provider *pp;
268	u_char *buf;
269	uint32_t magic1, magic2;
270	int error;
271
272	pp = cp->provider;
273
274	/* Sanity-check the provider. */
275	if (pp->sectorsize < sizeof(struct disklabel) ||
276	    pp->mediasize < BBSIZE)
277		return (ENOSPC);
278
279	/* Check that there's a disklabel. */
280	buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
281	if (buf == NULL)
282		return (error);
283	magic1 = le32dec(buf + 0);
284	magic2 = le32dec(buf + 132);
285	g_free(buf);
286	return ((magic1 == DISKMAGIC && magic2 == DISKMAGIC)
287	    ? G_PART_PROBE_PRI_NORM : ENXIO);
288}
289
290static int
291g_part_bsd_read(struct g_part_table *basetable, struct g_consumer *cp)
292{
293	struct g_provider *pp;
294	struct g_part_bsd_table *table;
295	struct g_part_entry *baseentry;
296	struct g_part_bsd_entry *entry;
297	struct partition part;
298	u_char *buf, *p;
299	off_t chs, msize;
300	u_int sectors, heads;
301	int error, index;
302
303	pp = cp->provider;
304	table = (struct g_part_bsd_table *)basetable;
305	msize = pp->mediasize / pp->sectorsize;
306
307	buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
308	if (buf == NULL)
309		return (error);
310
311	table->label = buf;
312
313	if (le32dec(buf + 40) != pp->sectorsize)
314		goto invalid_label;
315	sectors = le32dec(buf + 44);
316	if (sectors < 1 || sectors > 63)
317		goto invalid_label;
318	if (sectors != basetable->gpt_sectors) {
319		if (basetable->gpt_fixgeom)
320			goto invalid_label;
321		g_part_geometry_heads(msize, sectors, &chs, &heads);
322		if (chs == 0)
323			goto invalid_label;
324		basetable->gpt_sectors = sectors;
325		basetable->gpt_heads = heads;
326	}
327	heads = le32dec(buf + 48);
328	if (heads < 1 || heads > 255)
329		goto invalid_label;
330	if (heads != basetable->gpt_heads) {
331		if (basetable->gpt_fixgeom)
332			goto invalid_label;
333		basetable->gpt_heads = heads;
334	}
335	chs = le32dec(buf + 52) * heads * sectors;
336	if (chs < 1 || chs > msize)
337		goto invalid_label;
338
339	basetable->gpt_first = 0;
340	basetable->gpt_last = chs - 1;
341	basetable->gpt_isleaf = 1;
342
343	basetable->gpt_entries = le16dec(buf + 138);
344	if (basetable->gpt_entries < g_part_bsd_scheme.gps_minent ||
345	    basetable->gpt_entries > g_part_bsd_scheme.gps_maxent)
346		goto invalid_label;
347
348	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
349		p = buf + 148 + index * 16;
350		part.p_size = le32dec(p + 0);
351		part.p_offset = le32dec(p + 4);
352		part.p_fsize = le32dec(p + 8);
353		part.p_fstype = p[12];
354		part.p_frag = p[13];
355		part.p_cpg = le16dec(p + 14);
356		if (part.p_size == 0)
357			continue;
358		if (part.p_fstype == FS_UNUSED && index != RAW_PART)
359			continue;
360		if (part.p_offset < basetable->gpt_offset)
361			continue;
362		baseentry = g_part_new_entry(basetable, index + 1,
363		    part.p_offset - basetable->gpt_offset,
364		    part.p_offset - basetable->gpt_offset + part.p_size - 1);
365		entry = (struct g_part_bsd_entry *)baseentry;
366		entry->part = part;
367		if (part.p_fstype == FS_UNUSED)
368			baseentry->gpe_internal = 1;
369	}
370
371	return (0);
372
373 invalid_label:
374	printf("GEOM: %s: invalid disklabel.\n", pp->name);
375	g_free(table->label);
376	return (EINVAL);
377}
378
379static const char *
380g_part_bsd_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
381    char *buf, size_t bufsz)
382{
383	struct g_part_bsd_entry *entry;
384	int type;
385
386	entry = (struct g_part_bsd_entry *)baseentry;
387	type = entry->part.p_fstype;
388	if (type == FS_SWAP)
389		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
390	if (type == FS_BSDFFS)
391		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
392	if (type == FS_VINUM)
393		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
394	snprintf(buf, bufsz, "!%d", type);
395	return (buf);
396}
397
398static int
399g_part_bsd_write(struct g_part_table *basetable, struct g_consumer *cp)
400{
401	struct g_provider *pp;
402	struct g_part_entry *baseentry;
403	struct g_part_bsd_entry *entry;
404	struct g_part_bsd_table *table;
405	uint16_t sum;
406	u_char *p, *pe;
407	int error, index;
408
409	pp = cp->provider;
410	table = (struct g_part_bsd_table *)basetable;
411	baseentry = LIST_FIRST(&basetable->gpt_entry);
412	for (index = 1; index <= basetable->gpt_entries; index++) {
413		p = table->label + 148 + (index - 1) * 16;
414		entry = (baseentry != NULL && index == baseentry->gpe_index)
415		    ? (struct g_part_bsd_entry *)baseentry : NULL;
416		if (entry != NULL && !baseentry->gpe_deleted) {
417			le32enc(p + 0, entry->part.p_size);
418			le32enc(p + 4, entry->part.p_offset);
419			le32enc(p + 8, entry->part.p_fsize);
420			p[12] = entry->part.p_fstype;
421			p[13] = entry->part.p_frag;
422			le16enc(p + 14, entry->part.p_cpg);
423		} else
424			bzero(p, 16);
425
426		if (entry != NULL)
427			baseentry = LIST_NEXT(baseentry, gpe_entry);
428	}
429
430	/* Calculate checksum. */
431	le16enc(table->label + 136, 0);
432	pe = table->label + 148 + basetable->gpt_entries * 16;
433	sum = 0;
434	for (p = table->label; p < pe; p += 2)
435		sum ^= le16dec(p);
436	le16enc(table->label + 136, sum);
437
438	error = g_write_data(cp, pp->sectorsize, table->label, pp->sectorsize);
439	return (error);
440}
441