Home | History | Annotate | Download | only in diskomizer
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"@(#)vtoc.c	1.12	09/05/26 SMI"
     28 
     29 #include <diskomizer/vtoc.h>
     30 #include <diskomizer/log.h>
     31 #include "args.h"
     32 #include <string.h>
     33 #include <stdlib.h>
     34 #include <unistd.h>
     35 #include <stropts.h>
     36 #include <dlfcn.h>
     37 #include <errno.h>
     38 
     39 /*
     40  * Shamelessly stolen from vtoc.h so that this will compile on older
     41  * Operating systems and be able to take advantage of the new code when
     42  * it is available.
     43  */
     44 #ifndef V_EXTVERSION
     45 
     46 struct extpartition {
     47 	ushort_t p_tag;			/* ID tag of partition */
     48 	ushort_t p_flag;		/* permission flags */
     49 	ushort_t p_pad[2];
     50 	diskaddr_t p_start;		/* start sector no of partition */
     51 	diskaddr_t p_size;		/* # of blocks in partition */
     52 };
     53 
     54 struct extvtoc {
     55 	uint64_t	v_bootinfo[3];	/* info needed by mboot (unsupported) */
     56 	uint64_t	v_sanity;	/* to verify vtoc sanity */
     57 	uint64_t	v_version;	/* layout version */
     58 	char	v_volume[LEN_DKL_VVOL]; /* volume name */
     59 	ushort_t	v_sectorsz;	/* sector size in bytes */
     60 	ushort_t	v_nparts;	/* number of partitions */
     61 	ushort_t	pad[2];
     62 	uint64_t	v_reserved[10];
     63 	struct extpartition v_part[V_NUMPAR]; /* partition headers */
     64 	uint64_t timestamp[V_NUMPAR];	/* partition timestamp (unsupported) */
     65 	char	v_asciilabel[LEN_DKL_ASCII];	/* for compatibility */
     66 };
     67 
     68 extern int read_extvtoc(int, struct extvtoc *);
     69 #pragma weak read_extvtoc
     70 #define	READ_EXTVTOC(X, F, S) (read_extvtoc != NULL && \
     71 	(X = read_extvtoc((F), (S))) >= 0)
     72 #else
     73 #define	READ_EXTVTOC(X, F, S) ((X = read_extvtoc((F), (S))) >= 0)
     74 #endif
     75 #ifndef	DKIOCGEXTVTOC
     76 #define	DKIOCGEXTVTOC	(DKIOC|23)	/* Get extended VTOC */
     77 #endif
     78 #define	VTOC_USER_LIB "VTOC_USER_LIB"
     79 
     80 static struct disko_partition
     81 extvtoc_part_to_diskopart(struct extpartition *p)
     82 {
     83 	struct disko_partition dp;
     84 
     85 #define	P_TO_DP(A) dp.A = (uint64_t)p->A
     86 
     87 	P_TO_DP(p_tag);
     88 	P_TO_DP(p_flag);
     89 	P_TO_DP(p_start);
     90 	P_TO_DP(p_size);
     91 
     92 #undef	P_TO_DP
     93 	return (dp);
     94 }
     95 
     96 static struct disko_partition
     97 vtoc_part_to_diskopartition(struct partition *p)
     98 {
     99 	struct disko_partition dp;
    100 
    101 #define	P_TO_DP(A) dp.A = (uint64_t)p->A
    102 
    103 	P_TO_DP(p_tag);
    104 	P_TO_DP(p_flag);
    105 	P_TO_DP(p_start);
    106 	P_TO_DP(p_size);
    107 
    108 #undef	P_TO_DP
    109 	return (dp);
    110 }
    111 
    112 struct disko_vtoc *
    113 alloc_disko_vtoc(uint_t nparts)
    114 {
    115 	struct disko_vtoc *d;
    116 
    117 	d = ((struct disko_vtoc *) malloc(sizeof (struct disko_vtoc) +
    118 	    (nparts - 1) * sizeof (d->v_part[0])));
    119 	return (d);
    120 }
    121 
    122 static char *
    123 not_null_dlerror(void)
    124 {
    125 	static char no_error[] = "No error";
    126 	char *x;
    127 
    128 	x = dlerror();
    129 	return ((x == NULL) ? &no_error[0] : x);
    130 }
    131 
    132 static void *
    133 try_efi(int fd)
    134 {
    135 	struct dk_gpt *efi = NULL;
    136 	int i;
    137 	uint_t x;
    138 	void *handle;
    139 	struct disko_vtoc *dv;
    140 	short (*my_efi_alloc_and_read)(int fd, struct dk_gpt **);
    141 	void (*my_efi_free)(struct dk_gpt *);
    142 
    143 	if ((handle = dlopen("libefi.so.1", RTLD_NOW)) == NULL) {
    144 		plog(LOG_DEBUG, "%s\n", not_null_dlerror());
    145 		return (NULL);
    146 	}
    147 	my_efi_alloc_and_read = (short (*)(int, struct dk_gpt **))
    148 	    dlsym(handle, "efi_alloc_and_read");
    149 	my_efi_free = (void (*)(struct dk_gpt *))dlsym(handle,
    150 	    "efi_free");
    151 
    152 
    153 	if (my_efi_alloc_and_read == NULL ||
    154 	    ((int)(x = my_efi_alloc_and_read(fd, &efi))) < 0) {
    155 		plog(LOG_DEBUG, "%s\n", not_null_dlerror());
    156 		dlclose(handle);
    157 		return (NULL);
    158 	}
    159 
    160 	/*
    161 	 * Work around bug 4888423.  The Current version was reduced but
    162 	 * there could be efi labels out there with the incorrect
    163 	 * EFI_VERSION102
    164 	 */
    165 	if (efi->efi_version != EFI_VERSION_CURRENT &&
    166 	    !(efi->efi_version == EFI_VERSION102 &&
    167 	    opts.obscure_allow_efi_102)) {
    168 		if (efi->efi_version == EFI_VERSION102) {
    169 			plog(LOG_ERR,
    170 			    "EFI version mismatch. Got %#x expected %#x, "
    171 			    "try setting option obscure_allow_efi_102\n",
    172 			    efi->efi_version, EFI_VERSION_CURRENT);
    173 		} else {
    174 			plog(LOG_ERR,
    175 			    "EFI version mismatch. Got %#x expected %#x\n",
    176 			    efi->efi_version, EFI_VERSION_CURRENT);
    177 		}
    178 		dlclose(handle);
    179 		return (NULL);
    180 	}
    181 
    182 	for (i = 0; i <
    183 	    (sizeof (efi->efi_reserved) /
    184 	    sizeof (efi->efi_reserved[0])); i++) {
    185 		if (efi->efi_reserved[i] != 0) {
    186 			plog(LOG_WARNING,
    187 			    "efi->efi_reserved[%d] is not zero (%#x)\n", i,
    188 			    efi->efi_reserved[i]);
    189 			if (!opts.obscure_allow_non_zero_efi_reserved) {
    190 				dlclose(handle);
    191 				return (NULL);
    192 			}
    193 		}
    194 	}
    195 
    196 	if ((dv = alloc_disko_vtoc(efi->efi_nparts)) != NULL) {
    197 
    198 		dv->disko_vtoc_size = sizeof (*dv);
    199 		dv->disko_part_size = sizeof (dv->v_part[0]);
    200 
    201 		memset(&dv->v_volume[0], 0, sizeof (dv->v_volume));
    202 		memset(&dv->v_asciilabel[0], 0,
    203 		    sizeof (dv->v_asciilabel));
    204 
    205 		dv->v_sectorsz = efi->efi_lbasize;
    206 		dv->v_nparts = efi->efi_nparts;
    207 		dv->this_part = x;
    208 
    209 		for (i = 0; i < efi->efi_nparts; i++) {
    210 			dv->v_part[i].p_tag = efi->efi_parts[i].p_tag;
    211 			dv->v_part[i].p_flag = efi->efi_parts[i].p_flag;
    212 			dv->v_part[i].p_start =
    213 			    efi->efi_parts[i].p_start;
    214 			dv->v_part[i].p_size = efi->efi_parts[i].p_size;
    215 		}
    216 	}
    217 	if (my_efi_free) {
    218 		my_efi_free(efi);
    219 	}
    220 	dlclose(handle);
    221 	return (dv);
    222 }
    223 /*
    224  * Try to read the vtoc using a user supplied shared library containing the
    225  * symbol user_read_vtoc which should be a function that is of type
    226  * user_read_vtoc_t. If it is not a function this code will not fail
    227  * gracefully.
    228  *
    229  * The path to the users library is supplied as an option to diskomizer
    230  * with the VTOC_USER_LIB, option. Currently this means that this option
    231  * is not documented automatically like the others in the main diskomizer
    232  * code base.
    233  *
    234  * To prevent version problems it must fill in the first three fields of
    235  * the structure with some known good values which are in the header file
    236  * user_vtoc.h which it should be compiled with.
    237  *
    238  */
    239 static void *
    240 try_user_vtoc(int fd)
    241 {
    242 	static int have_opts;
    243 	static char *user_lib_name;
    244 
    245 	if (have_opts == 0) {
    246 		const struct option_ops *ops;
    247 		ops = opts_init();
    248 
    249 		if (ops->opt_str(VTOC_USER_LIB, &user_lib_name) == OPT_OK) {
    250 			user_lib_name = strdup(user_lib_name);
    251 		}
    252 		have_opts = 1;
    253 		opts_fini();
    254 	}
    255 	if (user_lib_name != NULL) {
    256 		user_read_vtoc_t my_user_read_vtoc;
    257 		void *handle;
    258 		struct disko_vtoc *dv;
    259 
    260 		if ((handle = dlopen(user_lib_name, RTLD_NOW)) == NULL) {
    261 			return (NULL);
    262 		}
    263 		if ((my_user_read_vtoc = (user_read_vtoc_t)dlsym(handle,
    264 		    "user_read_vtoc")) == NULL) {
    265 			dlclose(handle);
    266 			return (NULL);
    267 		}
    268 		dv = my_user_read_vtoc(fd);
    269 		dlclose(handle);
    270 		if (dv->disko_vtoc_version != DISKO_VTOC_VERSION) {
    271 			plog(LOG_WARNING,
    272 			    "%s:user_read_vtoc() returned incorrect version.\n",
    273 			    user_lib_name);
    274 			disko_vtoc_free(dv);
    275 			return (NULL);
    276 		}
    277 		if (dv->disko_vtoc_size != sizeof (*dv) ||
    278 		    dv->disko_part_size != sizeof (dv->v_part[0])) {
    279 			plog(LOG_WARNING,
    280 			    "%s:user_read_vtoc() returned incorrect "
    281 			    "structure size[s].\n", user_lib_name);
    282 			disko_vtoc_free(dv);
    283 			return (NULL);
    284 		}
    285 
    286 		return (dv);
    287 	}
    288 	return (NULL);
    289 }
    290 
    291 void *
    292 read_disko_vtoc(int fd)
    293 {
    294 	int x;
    295 	union {
    296 		struct vtoc vtoc;
    297 		struct extvtoc extvtoc;
    298 	} u;
    299 
    300 	if (READ_EXTVTOC(x, fd, &u.extvtoc) ||
    301 	    (x = ioctl(fd, DKIOCGEXTVTOC, &u.extvtoc)) >= 0) {
    302 		struct disko_vtoc *dv;
    303 		int i;
    304 
    305 		if ((dv = alloc_disko_vtoc(u.extvtoc.v_nparts)) == NULL) {
    306 			return (NULL);
    307 		}
    308 
    309 		dv->disko_vtoc_size = sizeof (*dv);
    310 		dv->disko_part_size = sizeof (dv->v_part[0]);
    311 
    312 		memcpy(dv->v_volume, u.extvtoc.v_volume, LEN_DKL_VVOL);
    313 		dv->v_volume[LEN_DKL_VVOL] = NULL;
    314 		memcpy(dv->v_asciilabel, u.extvtoc.v_asciilabel, LEN_DKL_ASCII);
    315 		dv->v_asciilabel[LEN_DKL_ASCII] = NULL;
    316 		dv->v_sectorsz = u.extvtoc.v_sectorsz;
    317 		dv->v_nparts = u.extvtoc.v_nparts;
    318 		dv->this_part = x;
    319 
    320 		for (i = 0; i < u.extvtoc.v_nparts; i++) {
    321 			dv->v_part[i] =
    322 			    extvtoc_part_to_diskopart(&u.extvtoc.v_part[i]);
    323 		}
    324 		return (dv);
    325 	} else if ((x = read_vtoc(fd, &u.vtoc)) >= 0 ||
    326 	    (x = ioctl(fd, DKIOCGVTOC, &u.vtoc)) >= 0) {
    327 		struct disko_vtoc *dv;
    328 		int i;
    329 
    330 		if ((dv = alloc_disko_vtoc(u.vtoc.v_nparts)) == NULL) {
    331 			return (NULL);
    332 		}
    333 
    334 		dv->disko_vtoc_size = sizeof (*dv);
    335 		dv->disko_part_size = sizeof (dv->v_part[0]);
    336 
    337 		memcpy(dv->v_volume, u.vtoc.v_volume, LEN_DKL_VVOL);
    338 		dv->v_volume[LEN_DKL_VVOL] = NULL;
    339 		memcpy(dv->v_asciilabel, u.vtoc.v_asciilabel, LEN_DKL_ASCII);
    340 		dv->v_asciilabel[LEN_DKL_ASCII] = NULL;
    341 		dv->v_sectorsz = u.vtoc.v_sectorsz;
    342 		dv->v_nparts = u.vtoc.v_nparts;
    343 		dv->this_part = x;
    344 
    345 		for (i = 0; i < u.vtoc.v_nparts; i++) {
    346 			dv->v_part[i] =
    347 			    vtoc_part_to_diskopartition(&u.vtoc.v_part[i]);
    348 		}
    349 		return (dv);
    350 	} else if ((x == -1) && (errno == ENOTSUP || errno == ENOTTY)) {
    351 		void * handle;
    352 
    353 		handle = try_efi(fd);
    354 		if (handle == NULL) {
    355 			handle = try_user_vtoc(fd);
    356 		}
    357 		return (handle);
    358 	} else {
    359 		return (NULL);
    360 	}
    361 }
    362 
    363 const char *
    364 disko_vtoc_volume(void *handle)
    365 {
    366 	struct disko_vtoc *vt = (struct disko_vtoc *)handle;
    367 
    368 	return (vt->v_volume);
    369 }
    370 
    371 const char *
    372 disko_vtoc_asciilabel(void *handle)
    373 {
    374 	struct disko_vtoc *vt = (struct disko_vtoc *)handle;
    375 
    376 	return (vt->v_asciilabel);
    377 }
    378 
    379 uint_t
    380 disko_vtoc_sectorsz(void *handle)
    381 {
    382 	struct disko_vtoc *vt = (struct disko_vtoc *)handle;
    383 
    384 	return (vt->v_sectorsz);
    385 }
    386 
    387 uint_t
    388 disko_vtoc_nparts(void *handle)
    389 {
    390 	struct disko_vtoc *vt = (struct disko_vtoc *)handle;
    391 
    392 	return (vt->v_nparts);
    393 }
    394 
    395 const struct disko_partition *
    396 disko_vtoc_parts(void *handle, uint_t part)
    397 {
    398 	struct disko_vtoc *vt = (struct disko_vtoc *)handle;
    399 
    400 	return (&vt->v_part[part]);
    401 }
    402 
    403 uint_t
    404 disko_vtoc_this_part(void *handle)
    405 {
    406 	struct disko_vtoc *vt = (struct disko_vtoc *)handle;
    407 
    408 	return (vt->this_part);
    409 }
    410 
    411 const struct disko_partition *
    412 disko_vtoc_this_partition(void *handle)
    413 {
    414 	return (disko_vtoc_parts(handle, disko_vtoc_this_part(handle)));
    415 }
    416 
    417 void
    418 disko_vtoc_free(void *handle)
    419 {
    420 	free(handle);
    421 }
    422