disk.c revision 241023
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/boot/common/disk.c 241023 2012-09-28 10:49:41Z ae $");
30
31#include <sys/disk.h>
32#include <stand.h>
33#include <stdarg.h>
34#include <bootstrap.h>
35#include <part.h>
36
37#include "disk.h"
38
39#ifdef DISK_DEBUG
40# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
41#else
42# define DEBUG(fmt, args...)
43#endif
44
45struct open_disk {
46	struct ptable		*table;
47	off_t			mediasize;
48	u_int			sectorsize;
49};
50
51struct print_args {
52	struct disk_devdesc	*dev;
53	const char		*prefix;
54	int			verbose;
55};
56
57/* Convert size to a human-readable number. */
58static char *
59display_size(uint64_t size, u_int sectorsize)
60{
61	static char buf[80];
62	char unit;
63
64	size = size * sectorsize / 1024;
65	unit = 'K';
66	if (size >= 10485760000LL) {
67		size /= 1073741824;
68		unit = 'T';
69	} else if (size >= 10240000) {
70		size /= 1048576;
71		unit = 'G';
72	} else if (size >= 10000) {
73		size /= 1024;
74		unit = 'M';
75	}
76	sprintf(buf, "%ld%cB", (long)size, unit);
77	return (buf);
78}
79
80static int
81ptblread(void *d, void *buf, size_t blocks, off_t offset)
82{
83	struct disk_devdesc *dev;
84	struct open_disk *od;
85
86	dev = (struct disk_devdesc *)d;
87	od = (struct open_disk *)dev->d_opendata;
88	return (dev->d_dev->dv_strategy(dev, F_READ, offset,
89	    blocks * od->sectorsize, (char *)buf, NULL));
90}
91
92#define	PWIDTH	35
93static void
94ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
95{
96	struct print_args *pa, bsd;
97	struct open_disk *od;
98	struct ptable *table;
99	char line[80];
100
101	pa = (struct print_args *)arg;
102	od = (struct open_disk *)pa->dev->d_opendata;
103	sprintf(line, "  %s%s: %s", pa->prefix, pname,
104	    parttype2str(part->type));
105	if (pa->verbose)
106		sprintf(line, "%-*s%s", PWIDTH, line,
107		    display_size(part->end - part->start + 1,
108		    od->sectorsize));
109	strcat(line, "\n");
110	pager_output(line);
111	if (part->type == PART_FREEBSD) {
112		/* Open slice with BSD label */
113		pa->dev->d_offset = part->start;
114		table = ptable_open(pa->dev, part->end - part->start + 1,
115		    od->sectorsize, ptblread);
116		if (table == NULL)
117			return;
118		sprintf(line, "  %s%s", pa->prefix, pname);
119		bsd.dev = pa->dev;
120		bsd.prefix = line;
121		bsd.verbose = pa->verbose;
122		ptable_iterate(table, &bsd, ptable_print);
123		ptable_close(table);
124	}
125}
126#undef PWIDTH
127
128void
129disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
130{
131	struct open_disk *od;
132	struct print_args pa;
133
134	/* Disk should be opened */
135	od = (struct open_disk *)dev->d_opendata;
136	pa.dev = dev;
137	pa.prefix = prefix;
138	pa.verbose = verbose;
139	ptable_iterate(od->table, &pa, ptable_print);
140}
141
142int
143disk_open(struct disk_devdesc *dev, off_t mediasize, u_int sectorsize)
144{
145	struct open_disk *od;
146	struct ptable *table;
147	struct ptable_entry part;
148	int rc;
149
150	od = (struct open_disk *)malloc(sizeof(struct open_disk));
151	if (od == NULL) {
152		DEBUG("no memory");
153		return (ENOMEM);
154	}
155	/*
156	 * While we are reading disk metadata, make sure we do it relative
157	 * to the start of the disk
158	 */
159	rc = 0;
160	table = NULL;
161	dev->d_offset = 0;
162	dev->d_opendata = od;
163	od->mediasize = mediasize;
164	od->sectorsize = sectorsize;
165	DEBUG("%s unit %d, slice %d, partition %d",
166	    disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition);
167
168	/* Determine disk layout. */
169	od->table = ptable_open(dev, mediasize / sectorsize, sectorsize,
170	    ptblread);
171	if (od->table == NULL) {
172		DEBUG("Can't read partition table");
173		rc = ENXIO;
174		goto out;
175	}
176	if (ptable_gettype(od->table) == PTABLE_BSD &&
177	    dev->d_partition >= 0) {
178		/* It doesn't matter what value has d_slice */
179		rc = ptable_getpart(od->table, &part, dev->d_partition);
180		if (rc == 0)
181			dev->d_offset = part.start;
182	} else if (dev->d_slice >= 0) {
183		/* Try to get information about partition */
184		if (dev->d_slice == 0)
185			rc = ptable_getbestpart(od->table, &part);
186		else
187			rc = ptable_getpart(od->table, &part, dev->d_slice);
188		if (rc != 0) /* Partition doesn't exist */
189			goto out;
190		dev->d_offset = part.start;
191		if (dev->d_slice == 0) {
192			/* Save the slice number of best partition to dev */
193			dev->d_slice = part.index;
194			if (ptable_gettype(od->table) == PTABLE_GPT)
195				dev->d_partition = 255;
196		}
197		if (dev->d_partition == 255)
198			goto out; /* Nothing more to do */
199		/*
200		 * If d_partition < 0 and we are looking at a BSD slice,
201		 * then try to read BSD label, otherwise return the
202		 * whole MBR slice.
203		 */
204		if (dev->d_partition == -1 &&
205		    part.type != PART_FREEBSD)
206			goto out;
207		/* Try to read BSD label */
208		table = ptable_open(dev, part.end - part.start + 1,
209		    od->sectorsize, ptblread);
210		if (table == NULL) {
211			DEBUG("Can't read BSD label");
212			rc = ENXIO;
213			goto out;
214		}
215		/*
216		 * If slice contains BSD label and d_partition < 0, then
217		 * assume the 'a' partition. Otherwise just return the
218		 * whole MBR slice, because it can contain ZFS.
219		 */
220		if (dev->d_partition < 0) {
221			if (ptable_gettype(table) != PTABLE_BSD)
222				goto out;
223			dev->d_partition = 0;
224		}
225		rc = ptable_getpart(table, &part, dev->d_partition);
226		if (rc != 0)
227			goto out;
228		dev->d_offset += part.start;
229	}
230out:
231	if (table != NULL)
232		ptable_close(table);
233
234	if (rc != 0) {
235		if (od->table != NULL)
236			ptable_close(od->table);
237		free(od);
238		DEBUG("%s could not open", disk_fmtdev(dev));
239	} else {
240		DEBUG("%s offset %lld", disk_fmtdev(dev), dev->d_offset);
241	}
242	return (rc);
243}
244
245int
246disk_close(struct disk_devdesc *dev)
247{
248	struct open_disk *od;
249
250	od = (struct open_disk *)dev->d_opendata;
251	DEBUG("%s closed", disk_fmtdev(dev));
252	ptable_close(od->table);
253	free(od);
254	return (0);
255}
256
257char*
258disk_fmtdev(struct disk_devdesc *dev)
259{
260	static char buf[128];
261	char *cp;
262
263	cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit);
264	if (dev->d_slice > 0) {
265#ifdef LOADER_GPT_SUPPORT
266		if (dev->d_partition == 255) {
267			sprintf(cp, "p%d:", dev->d_slice);
268			return (buf);
269		} else
270#endif
271#ifdef LOADER_MBR_SUPPORT
272			cp += sprintf(cp, "s%d", dev->d_slice);
273#endif
274	}
275	if (dev->d_partition >= 0)
276		cp += sprintf(cp, "%c", dev->d_partition + 'a');
277	strcat(cp, ":");
278	return (buf);
279}
280
281int
282disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path)
283{
284	int unit, slice, partition;
285	const char *np;
286	char *cp;
287
288	np = devspec;
289	unit = slice = partition = -1;
290	if (*np != '\0' && *np != ':') {
291		unit = strtol(np, &cp, 10);
292		if (cp == np)
293			return (EUNIT);
294#ifdef LOADER_GPT_SUPPORT
295		if (*cp == 'p') {
296			np = cp + 1;
297			slice = strtol(np, &cp, 10);
298			if (np == cp)
299				return (ESLICE);
300			/* we don't support nested partitions on GPT */
301			if (*cp != '\0' && *cp != ':')
302				return (EINVAL);
303			partition = 255;
304		} else
305#endif
306#ifdef LOADER_MBR_SUPPORT
307		if (*cp == 's') {
308			np = cp + 1;
309			slice = strtol(np, &cp, 10);
310			if (np == cp)
311				return (ESLICE);
312		}
313#endif
314		if (*cp != '\0' && *cp != ':') {
315			partition = *cp - 'a';
316			if (partition < 0)
317				return (EPART);
318			cp++;
319		}
320	} else
321		return (EINVAL);
322
323	if (*cp != '\0' && *cp != ':')
324		return (EINVAL);
325	dev->d_unit = unit;
326	dev->d_slice = slice;
327	dev->d_partition = partition;
328	if (path != NULL)
329		*path = (*cp == '\0') ? cp: cp + 1;
330	return (0);
331}
332