Home | History | Annotate | Download | only in common
      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 #include <fcntl.h>
     28 #include <libdevinfo.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <string.h>
     32 #include <strings.h>
     33 #include <stropts.h>
     34 #include <sys/dkio.h>
     35 #include <sys/sunddi.h>
     36 #include <sys/types.h>
     37 #include <unistd.h>
     38 #include <sys/vtoc.h>
     39 #include <sys/efi_partition.h>
     40 
     41 #include "libdiskmgt.h"
     42 #include "disks_private.h"
     43 #include "partition.h"
     44 
     45 #define	IOCTLRETRIES		2
     46 #define	IOCTLRETRYINTERVAL	1
     47 
     48 static descriptor_t	**apply_filter(descriptor_t **media, int filter[],
     49 			    int *errp);
     50 static int		get_attrs(disk_t *dp, int fd, nvlist_t *attrs);
     51 static int		get_rmm_name(disk_t *dp, char *mname, int size);
     52 static int		get_media_type(uint_t media_type);
     53 static int		desc_ok(descriptor_t *dp);
     54 
     55 /*
     56  * This function gets the descriptors we are associated with.
     57  */
     58 descriptor_t **
     59 media_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
     60     int *errp)
     61 {
     62 	if (!desc_ok(desc)) {
     63 		*errp = ENODEV;
     64 		return (NULL);
     65 	}
     66 
     67 	switch (type) {
     68 	case DM_DRIVE:
     69 		return (drive_get_assocs(desc, errp));
     70 	case DM_PARTITION:
     71 		return (partition_get_assocs(desc, errp));
     72 	case DM_SLICE:
     73 		return (slice_get_assocs(desc, errp));
     74 	}
     75 
     76 	*errp = EINVAL;
     77 	return (NULL);
     78 }
     79 
     80 /*
     81  * Get the media descriptors for the given drive/partition/slice.
     82  */
     83 descriptor_t **
     84 media_get_assocs(descriptor_t *dp, int *errp)
     85 {
     86 	descriptor_t	**media;
     87 	char		mname[MAXPATHLEN];
     88 
     89 	if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
     90 		/*
     91 		 * For drives, this means no media but slice/part.
     92 		 * require media.
     93 		 */
     94 		if (dp->type == DM_DRIVE) {
     95 			return (libdiskmgt_empty_desc_array(errp));
     96 		} else {
     97 			*errp = ENODEV;
     98 			return (NULL);
     99 		}
    100 	}
    101 
    102 	/* make the snapshot */
    103 	media = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
    104 	if (media == NULL) {
    105 		*errp = ENOMEM;
    106 		return (NULL);
    107 	}
    108 
    109 	media[0] = cache_get_desc(DM_MEDIA, dp->p.disk, mname, NULL, errp);
    110 	if (*errp != 0) {
    111 		free(media);
    112 		return (NULL);
    113 	}
    114 	media[1] = NULL;
    115 
    116 	*errp = 0;
    117 	return (media);
    118 }
    119 
    120 nvlist_t *
    121 media_get_attributes(descriptor_t *dp, int *errp)
    122 {
    123 	nvlist_t	*attrs = NULL;
    124 	int		fd;
    125 
    126 	if (!desc_ok(dp)) {
    127 		*errp = ENODEV;
    128 		return (NULL);
    129 	}
    130 
    131 	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
    132 		*errp = ENOMEM;
    133 		return (NULL);
    134 	}
    135 
    136 	fd = drive_open_disk(dp->p.disk, NULL, 0);
    137 
    138 	if ((*errp = get_attrs(dp->p.disk, fd, attrs)) != 0) {
    139 		nvlist_free(attrs);
    140 		attrs = NULL;
    141 	}
    142 
    143 	if (fd >= 0) {
    144 		(void) close(fd);
    145 	}
    146 
    147 	return (attrs);
    148 }
    149 
    150 descriptor_t *
    151 media_get_descriptor_by_name(char *name, int *errp)
    152 {
    153 	descriptor_t	**media;
    154 	int		i;
    155 	descriptor_t	*medium = NULL;
    156 
    157 	media = cache_get_descriptors(DM_MEDIA, errp);
    158 	if (*errp != 0) {
    159 		return (NULL);
    160 	}
    161 
    162 	for (i = 0; media[i]; i++) {
    163 		if (libdiskmgt_str_eq(name, media[i]->name)) {
    164 			medium = media[i];
    165 		} else {
    166 			/* clean up the unused descriptors */
    167 			cache_free_descriptor(media[i]);
    168 		}
    169 	}
    170 	free(media);
    171 
    172 	if (medium == NULL) {
    173 		*errp = ENODEV;
    174 	}
    175 
    176 	return (medium);
    177 }
    178 
    179 descriptor_t **
    180 media_get_descriptors(int filter[], int *errp)
    181 {
    182 	descriptor_t	**media;
    183 
    184 	media = cache_get_descriptors(DM_MEDIA, errp);
    185 	if (*errp != 0) {
    186 		return (NULL);
    187 	}
    188 
    189 	if (filter != NULL && filter[0] != DM_FILTER_END) {
    190 		descriptor_t	**found;
    191 
    192 		found = apply_filter(media, filter, errp);
    193 		if (*errp != 0) {
    194 			media = NULL;
    195 		} else {
    196 			media = found;
    197 		}
    198 	}
    199 
    200 	return (media);
    201 }
    202 
    203 char *
    204 media_get_name(descriptor_t *desc)
    205 {
    206 	return (desc->name);
    207 }
    208 
    209 /* ARGSUSED */
    210 nvlist_t *
    211 media_get_stats(descriptor_t *dp, int stat_type, int *errp)
    212 {
    213 	/* There are no stat types defined for media */
    214 	*errp = EINVAL;
    215 	return (NULL);
    216 }
    217 
    218 int
    219 media_make_descriptors()
    220 {
    221 	int		error;
    222 	disk_t		*dp;
    223 	char		mname[MAXPATHLEN];
    224 
    225 	dp = cache_get_disklist();
    226 	while (dp != NULL) {
    227 		if (media_read_name(dp, mname, sizeof (mname))) {
    228 			cache_load_desc(DM_MEDIA, dp, mname, NULL, &error);
    229 			if (error != 0) {
    230 				return (error);
    231 			}
    232 		}
    233 
    234 		dp = dp->next;
    235 	}
    236 
    237 	return (0);
    238 }
    239 
    240 /*
    241  * Read the media information.
    242  */
    243 int
    244 media_read_info(int fd, struct dk_minfo *minfo)
    245 {
    246 	int	status;
    247 	int	tries = 0;
    248 
    249 	minfo->dki_media_type = 0;
    250 
    251 	/*
    252 	 * This ioctl can fail if the media is not loaded or spun up.
    253 	 * Retrying can sometimes succeed since the first ioctl will have
    254 	 * started the media before the ioctl timed out so the media may be
    255 	 * spun up on the subsequent attempt.
    256 	 */
    257 	while ((status = ioctl(fd, DKIOCGMEDIAINFO, minfo)) < 0) {
    258 		tries++;
    259 		if (tries >= IOCTLRETRIES) {
    260 			break;
    261 		}
    262 		(void) sleep(IOCTLRETRYINTERVAL);
    263 	}
    264 
    265 	if (status < 0) {
    266 		return (0);
    267 	}
    268 
    269 	return (1);
    270 }
    271 
    272 /* return 1 if there is media, 0 if not. */
    273 int
    274 media_read_name(disk_t *dp, char *mname, int size)
    275 {
    276 	mname[0] = 0;
    277 
    278 	if (!dp->removable) {
    279 		/* not removable, so media name is devid */
    280 		if (dp->device_id != NULL) {
    281 			(void) strlcpy(mname, dp->device_id, size);
    282 		}
    283 		return (1);
    284 	}
    285 
    286 	/* This is a removable media drive. */
    287 	return (get_rmm_name(dp, mname, size));
    288 }
    289 
    290 static descriptor_t **
    291 apply_filter(descriptor_t **media, int filter[], int *errp)
    292 {
    293 	descriptor_t	**found;
    294 	int		i;
    295 	int		cnt = 0;
    296 	int		pos;
    297 
    298 	/* count the number of media in the snapshot */
    299 	for (i = 0; media[i]; i++) {
    300 		cnt++;
    301 	}
    302 
    303 	found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
    304 	if (found == NULL) {
    305 		*errp = ENOMEM;
    306 		cache_free_descriptors(media);
    307 		return (NULL);
    308 	}
    309 
    310 	pos = 0;
    311 	for (i = 0; media[i]; i++) {
    312 		int	fd;
    313 		struct	dk_minfo minfo;
    314 
    315 		if ((fd = drive_open_disk(media[i]->p.disk, NULL, 0)) < 0) {
    316 			continue;
    317 		}
    318 
    319 		if (media_read_info(fd, &minfo)) {
    320 			int	mtype;
    321 			int	j;
    322 			int	match;
    323 
    324 			mtype = get_media_type(minfo.dki_media_type);
    325 
    326 			match = 0;
    327 			for (j = 0; filter[j] != DM_FILTER_END; j++) {
    328 				if (mtype == filter[j]) {
    329 					found[pos++] = media[i];
    330 					match = 1;
    331 					break;
    332 				}
    333 			}
    334 
    335 			if (!match) {
    336 				cache_free_descriptor(media[i]);
    337 			}
    338 		}
    339 		(void) close(fd);
    340 	}
    341 	found[pos] = NULL;
    342 	free(media);
    343 
    344 	*errp = 0;
    345 	return (found);
    346 }
    347 
    348 /* return 1 if the media descriptor is still valid, 0 if not. */
    349 static int
    350 desc_ok(descriptor_t *dp)
    351 {
    352 	/* First verify the media name for removable media */
    353 	if (dp->p.disk->removable) {
    354 		char	mname[MAXPATHLEN];
    355 
    356 		if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
    357 			return (0);
    358 		}
    359 
    360 		if (mname[0] == 0) {
    361 			return (libdiskmgt_str_eq(dp->name, NULL));
    362 		} else {
    363 			return (libdiskmgt_str_eq(dp->name, mname));
    364 		}
    365 	}
    366 
    367 	return (1);
    368 }
    369 
    370 static int
    371 get_attrs(disk_t *dp, int fd, nvlist_t *attrs)
    372 {
    373 	struct	dk_minfo minfo;
    374 	struct	dk_geom	geometry;
    375 
    376 	if (fd < 0) {
    377 		return (ENODEV);
    378 	}
    379 
    380 	bzero(&minfo, sizeof (struct dk_minfo));
    381 
    382 	/* The first thing to do is read the media */
    383 	if (!media_read_info(fd, &minfo)) {
    384 		return (ENODEV);
    385 	}
    386 
    387 	if (partition_has_fdisk(dp, fd)) {
    388 		if (nvlist_add_boolean(attrs, DM_FDISK) != 0) {
    389 			return (ENOMEM);
    390 		}
    391 	}
    392 
    393 	if (dp->removable) {
    394 		if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) {
    395 			return (ENOMEM);
    396 		}
    397 
    398 		if (nvlist_add_boolean(attrs, DM_LOADED) != 0) {
    399 			return (ENOMEM);
    400 		}
    401 	}
    402 
    403 	if (nvlist_add_uint64(attrs, DM_SIZE, minfo.dki_capacity) != 0) {
    404 		return (ENOMEM);
    405 	}
    406 
    407 	if (nvlist_add_uint32(attrs, DM_BLOCKSIZE, minfo.dki_lbsize) != 0) {
    408 		return (ENOMEM);
    409 	}
    410 
    411 	if (nvlist_add_uint32(attrs, DM_MTYPE,
    412 	    get_media_type(minfo.dki_media_type)) != 0) {
    413 		return (ENOMEM);
    414 	}
    415 
    416 	/* only for disks < 1TB  and x86 */
    417 #if defined(i386) || defined(__amd64)
    418 	if (ioctl(fd, DKIOCG_PHYGEOM, &geometry) >= 0) {
    419 #else
    420 	/* sparc call */
    421 	if (ioctl(fd, DKIOCGGEOM, &geometry) >= 0) {
    422 #endif
    423 		struct extvtoc	vtoc;
    424 
    425 		if (nvlist_add_uint64(attrs, DM_START, 0) != 0) {
    426 			return (ENOMEM);
    427 		}
    428 		if (nvlist_add_uint64(attrs, DM_NACCESSIBLE,
    429 		    geometry.dkg_ncyl * geometry.dkg_nhead * geometry.dkg_nsect)
    430 		    != 0) {
    431 			return (ENOMEM);
    432 		}
    433 		if (nvlist_add_uint32(attrs, DM_NCYLINDERS, geometry.dkg_ncyl)
    434 		    != 0) {
    435 			return (ENOMEM);
    436 		}
    437 		if (nvlist_add_uint32(attrs, DM_NPHYSCYLINDERS,
    438 		    geometry.dkg_pcyl) != 0) {
    439 			return (ENOMEM);
    440 		}
    441 		if (nvlist_add_uint32(attrs, DM_NALTCYLINDERS,
    442 		    geometry.dkg_acyl) != 0) {
    443 			return (ENOMEM);
    444 		}
    445 		if (nvlist_add_uint32(attrs, DM_NHEADS,
    446 		    geometry.dkg_nhead) != 0) {
    447 			return (ENOMEM);
    448 		}
    449 		if (nvlist_add_uint32(attrs, DM_NSECTORS, geometry.dkg_nsect)
    450 		    != 0) {
    451 			return (ENOMEM);
    452 		}
    453 		if (nvlist_add_uint32(attrs, DM_NACTUALCYLINDERS,
    454 		    geometry.dkg_ncyl) != 0) {
    455 			return (ENOMEM);
    456 		}
    457 
    458 		if (read_extvtoc(fd, &vtoc) >= 0 && vtoc.v_volume[0] != 0) {
    459 			char	label[LEN_DKL_VVOL + 1];
    460 
    461 			(void) snprintf(label, sizeof (label), "%.*s",
    462 			    LEN_DKL_VVOL, vtoc.v_volume);
    463 			if (nvlist_add_string(attrs, DM_LABEL, label) != 0) {
    464 				return (ENOMEM);
    465 			}
    466 		}
    467 
    468 	} else {
    469 		/* check for disks > 1TB for accessible size */
    470 		struct dk_gpt	*efip;
    471 
    472 		if (efi_alloc_and_read(fd, &efip) >= 0) {
    473 			diskaddr_t	p8size = 0;
    474 
    475 			if (nvlist_add_boolean(attrs, DM_EFI) != 0) {
    476 				return (ENOMEM);
    477 			}
    478 			if (nvlist_add_uint64(attrs, DM_START,
    479 			    efip->efi_first_u_lba) != 0) {
    480 				return (ENOMEM);
    481 			}
    482 			/* partition 8 is reserved on EFI labels */
    483 			if (efip->efi_nparts >= 9) {
    484 				p8size = efip->efi_parts[8].p_size;
    485 			}
    486 			if (nvlist_add_uint64(attrs, DM_NACCESSIBLE,
    487 			    (efip->efi_last_u_lba - p8size) -
    488 			    efip->efi_first_u_lba) != 0) {
    489 				efi_free(efip);
    490 				return (ENOMEM);
    491 			}
    492 			efi_free(efip);
    493 		}
    494 	}
    495 	return (0);
    496 }
    497 
    498 static int
    499 get_media_type(uint_t media_type)
    500 {
    501 	switch (media_type) {
    502 	case DK_UNKNOWN:
    503 		return (DM_MT_UNKNOWN);
    504 	case DK_MO_ERASABLE:
    505 		return (DM_MT_MO_ERASABLE);
    506 	case DK_MO_WRITEONCE:
    507 		return (DM_MT_MO_WRITEONCE);
    508 	case DK_AS_MO:
    509 		return (DM_MT_AS_MO);
    510 	case DK_CDROM:
    511 		return (DM_MT_CDROM);
    512 	case DK_CDR:
    513 		return (DM_MT_CDR);
    514 	case DK_CDRW:
    515 		return (DM_MT_CDRW);
    516 	case DK_DVDROM:
    517 		return (DM_MT_DVDROM);
    518 	case DK_DVDR:
    519 		return (DM_MT_DVDR);
    520 	case DK_DVDRAM:
    521 		return (DM_MT_DVDRAM);
    522 	case DK_FIXED_DISK:
    523 		return (DM_MT_FIXED);
    524 	case DK_FLOPPY:
    525 		return (DM_MT_FLOPPY);
    526 	case DK_ZIP:
    527 		return (DM_MT_ZIP);
    528 	case DK_JAZ:
    529 		return (DM_MT_JAZ);
    530 	default:
    531 		return (DM_MT_UNKNOWN);
    532 	}
    533 }
    534 
    535 /*
    536  * This function handles removable media.
    537  */
    538 static int
    539 get_rmm_name(disk_t *dp, char *mname, int size)
    540 {
    541 	int		loaded;
    542 	int		fd;
    543 
    544 	loaded = 0;
    545 
    546 	if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) {
    547 		struct dk_minfo minfo;
    548 
    549 		if ((loaded = media_read_info(fd, &minfo))) {
    550 			struct extvtoc vtoc;
    551 
    552 			if (read_extvtoc(fd, &vtoc) >= 0) {
    553 				if (vtoc.v_volume[0] != NULL) {
    554 					if (LEN_DKL_VVOL < size) {
    555 						(void) strlcpy(mname,
    556 						    vtoc.v_volume,
    557 						    LEN_DKL_VVOL);
    558 					} else {
    559 						(void) strlcpy(mname,
    560 						    vtoc.v_volume, size);
    561 					}
    562 				}
    563 			}
    564 		}
    565 
    566 		(void) close(fd);
    567 	}
    568 
    569 	return (loaded);
    570 }
    571