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