1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*LINTLIBRARY*/
28
29
30#include <stdio.h>
31#include <errno.h>
32#include <memory.h>
33#include <unistd.h>
34#include <sys/types.h>
35#include <sys/param.h>
36#include <sys/dkio.h>
37#include <sys/vtoc.h>
38#include <strings.h>
39#include <limits.h>
40
41/*
42 * To copy each field of vtoc individually for copying extvtoc
43 * to 32 bit vtoc and vs.
44 * Currently bootinfo and timestamp are not really supported.
45 */
46
47#define	libadm_vtoc_copy(vs, vd) \
48	{							\
49	int i;							\
50	vd->v_bootinfo[0]	= (unsigned)vs->v_bootinfo[0];	\
51	vd->v_bootinfo[1]	= (unsigned)vs->v_bootinfo[1];	\
52	vd->v_bootinfo[2]	= (unsigned)vs->v_bootinfo[2];	\
53	vd->v_sanity		= (unsigned)vs->v_sanity;	\
54	vd->v_version		= (unsigned)vs->v_version;	\
55	bcopy(vs->v_volume, vd->v_volume, LEN_DKL_VVOL);	\
56	vd->v_sectorsz		= vs->v_sectorsz;		\
57	vd->v_nparts		= vs->v_nparts;			\
58	vd->v_version		= (unsigned)vs->v_version;	\
59	for (i = 0; i < 10; i++)				\
60		vd->v_reserved[i] = (unsigned)vs->v_reserved[i];\
61	for (i = 0; i < V_NUMPAR; i++) {			\
62		vd->v_part[i].p_tag = vs->v_part[i].p_tag;	\
63		vd->v_part[i].p_flag = vs->v_part[i].p_flag;	\
64		vd->v_part[i].p_start = (unsigned)vs->v_part[i].p_start;\
65		vd->v_part[i].p_size = (unsigned)vs->v_part[i].p_size;	\
66	}								\
67	for (i = 0; i < V_NUMPAR; i++)					\
68		if ((sizeof (vd->timestamp[i]) != sizeof (vs->timestamp[i])) &&\
69		    (vs->timestamp[i] > INT32_MAX))			\
70			vd->timestamp[i] = INT32_MAX;			\
71		else							\
72			vd->timestamp[i] = (unsigned)vs->timestamp[i];	\
73	bcopy(vs->v_asciilabel, vd->v_asciilabel, LEN_DKL_ASCII);	\
74	}
75
76
77/*
78 * Read VTOC - return partition number.
79 */
80int
81read_vtoc(int fd, struct vtoc *vtoc)
82{
83	struct dk_cinfo		dki_info;
84
85	/*
86	 * Read the vtoc.
87	 */
88	if (ioctl(fd, DKIOCGVTOC, (caddr_t)vtoc) == -1) {
89		switch (errno) {
90		case EIO:
91			return (VT_EIO);
92		case EINVAL:
93			return (VT_EINVAL);
94		case ENOTSUP:
95			/* GPT labeled or disk > 1TB with no extvtoc support */
96			return (VT_ENOTSUP);
97		case EOVERFLOW:
98			return (VT_EOVERFLOW);
99		default:
100			return (VT_ERROR);
101		}
102	}
103
104	/*
105	 * Sanity-check the vtoc.
106	 */
107	if (vtoc->v_sanity != VTOC_SANE) {
108		return (VT_EINVAL);
109	}
110
111	/*
112	 * Convert older-style vtoc's.
113	 */
114	switch (vtoc->v_version) {
115	case 0:
116		/*
117		 * No vtoc information.  Install default
118		 * nparts/sectorsz and version.  We are
119		 * assuming that the driver returns the
120		 * current partition information correctly.
121		 */
122
123		vtoc->v_version = V_VERSION;
124		if (vtoc->v_nparts == 0)
125			vtoc->v_nparts = V_NUMPAR;
126		if (vtoc->v_sectorsz == 0)
127			vtoc->v_sectorsz = DEV_BSIZE;
128
129		break;
130
131	case V_VERSION:
132		break;
133
134	default:
135		return (VT_EINVAL);
136	}
137
138	/*
139	 * Return partition number for this file descriptor.
140	 */
141	if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
142		switch (errno) {
143		case EIO:
144			return (VT_EIO);
145		case EINVAL:
146			return (VT_EINVAL);
147		default:
148			return (VT_ERROR);
149		}
150	}
151	if (dki_info.dki_partition > V_NUMPAR) {
152		return (VT_EINVAL);
153	}
154	return ((int)dki_info.dki_partition);
155}
156
157/*
158 * Write VTOC
159 */
160int
161write_vtoc(int fd, struct vtoc *vtoc)
162{
163	int i;
164	/*
165	 * Sanity-check the vtoc
166	 */
167	if (vtoc->v_sanity != VTOC_SANE || vtoc->v_nparts > V_NUMPAR) {
168		return (-1);
169	}
170
171	/*
172	 * since many drivers won't allow opening a device make sure
173	 * all partitions aren't being set to zero. If all are zero then
174	 * we have no way to set them to something else
175	 */
176
177	for (i = 0; i < (int)vtoc->v_nparts; i++)
178		if (vtoc->v_part[i].p_size > 0)
179			break;
180	if (i == (int)vtoc->v_nparts)
181		return (-1);
182
183	/*
184	 * Write the vtoc
185	 */
186	if (ioctl(fd, DKIOCSVTOC, (caddr_t)vtoc) == -1) {
187		switch (errno) {
188		case EIO:
189			return (VT_EIO);
190		case EINVAL:
191			return (VT_EINVAL);
192		case ENOTSUP:
193			/* GPT labeled or disk > 1TB with no extvtoc support */
194			return (VT_ENOTSUP);
195		case EOVERFLOW:
196			return (VT_EOVERFLOW);
197		default:
198			return (VT_ERROR);
199		}
200	}
201	return (0);
202}
203
204int
205read_extvtoc(int fd, struct extvtoc *extvtoc)
206{
207	struct dk_cinfo		dki_info;
208	struct vtoc	oldvtoc;
209	struct vtoc *oldvtocp = &oldvtoc;
210	int ret;
211
212	/*
213	 * Read the vtoc.
214	 */
215	if (ioctl(fd, DKIOCGEXTVTOC, (caddr_t)extvtoc) == -1) {
216		switch (errno) {
217		case EIO:
218			return (VT_EIO);
219		case EINVAL:
220			return (VT_EINVAL);
221		/* for disks > 1TB */
222		case ENOTSUP:
223			return (VT_ENOTSUP);
224		case EOVERFLOW:
225			return (VT_EOVERFLOW);
226		case ENOTTY:
227
228			if ((ret = read_vtoc(fd, oldvtocp)) < 0)
229				return (ret);
230
231#ifdef _LP64
232			/*
233			 * 64-bit vtoc and extvtoc have the same field sizes
234			 * and offsets.
235			 */
236			bcopy(oldvtocp, extvtoc, sizeof (struct extvtoc));
237#else
238			bzero(extvtoc, sizeof (struct extvtoc));
239			libadm_vtoc_copy(oldvtocp, extvtoc);
240#endif
241			return (ret);
242
243
244		default:
245			return (VT_ERROR);
246		}
247	}
248
249	/*
250	 * Sanity-check the vtoc.
251	 */
252	if (extvtoc->v_sanity != VTOC_SANE) {
253		return (VT_EINVAL);
254	}
255
256	switch (extvtoc->v_version) {
257	case 0:
258		/*
259		 * For pre-version 1 vtoc keep same functionality
260		 * as read_vtoc.
261		 */
262
263		extvtoc->v_version = V_VERSION;
264		if (extvtoc->v_nparts == 0)
265			extvtoc->v_nparts = V_NUMPAR;
266		if (extvtoc->v_sectorsz == 0)
267			extvtoc->v_sectorsz = DEV_BSIZE;
268
269		break;
270
271	case V_VERSION:
272		break;
273
274	default:
275		return (VT_EINVAL);
276	}
277
278	/*
279	 * Return partition number for this file descriptor.
280	 */
281	if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
282		switch (errno) {
283		case EIO:
284			return (VT_EIO);
285		case EINVAL:
286			return (VT_EINVAL);
287		default:
288			return (VT_ERROR);
289		}
290	}
291	if (dki_info.dki_partition > V_NUMPAR) {
292		return (VT_EINVAL);
293	}
294	return ((int)dki_info.dki_partition);
295}
296
297/*
298 * Write ext VTOC.
299 */
300int
301write_extvtoc(int fd, struct extvtoc *extvtoc)
302{
303	int i;
304	struct vtoc	oldvtoc;
305	struct vtoc	*oldvtocp = &oldvtoc;
306	/*
307	 * Sanity-check the vtoc
308	 */
309	if (extvtoc->v_sanity != VTOC_SANE || extvtoc->v_nparts > V_NUMPAR) {
310		return (-1);
311	}
312
313	/*
314	 * since many drivers won't allow opening a device make sure
315	 * all partitions aren't being set to zero. If all are zero then
316	 * we have no way to set them to something else
317	 */
318
319	for (i = 0; i < (int)extvtoc->v_nparts; i++)
320		if (extvtoc->v_part[i].p_size > 0)
321			break;
322	if (i == (int)extvtoc->v_nparts)
323		return (-1);
324
325	/*
326	 * Write the extvtoc
327	 */
328	if (ioctl(fd, DKIOCSEXTVTOC, (caddr_t)extvtoc) == -1) {
329		switch (errno) {
330		case EIO:
331			return (VT_EIO);
332		case EINVAL:
333			return (VT_EINVAL);
334		/* for disks > 1TB */
335		case ENOTSUP:
336			return (VT_ENOTSUP);
337		case EOVERFLOW:
338			return (VT_EOVERFLOW);
339		case ENOTTY:
340#ifdef _LP64
341			/*
342			 * 64-bit vtoc and extvtoc have the same field sizes
343			 * and offsets.
344			 */
345			bcopy(extvtoc, oldvtocp, sizeof (struct vtoc));
346#else
347			bzero(oldvtocp, sizeof (struct vtoc));
348			libadm_vtoc_copy(extvtoc, oldvtocp);
349
350#endif
351			return (write_vtoc(fd, &oldvtoc));
352
353		default:
354			return (VT_ERROR);
355		}
356	}
357
358	return (0);
359}
360