xref: /illumos-gate/usr/src/lib/libdiskmgt/common/media.c (revision 342440ec94087b8c751c580ab9ed6c693d31d418)
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