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
47static descriptor_t	**apply_filter(descriptor_t **media, int filter[],
48			    int *errp);
49static int		get_attrs(disk_t *dp, int fd, nvlist_t *attrs);
50static int		get_rmm_name(disk_t *dp, char *mname, int size);
51static int		get_media_type(uint_t media_type);
52static int		desc_ok(descriptor_t *dp);
53
54/*
55 * This function gets the descriptors we are associated with.
56 */
57descriptor_t **
58media_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 */
82descriptor_t **
83media_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
119nvlist_t *
120media_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
149descriptor_t *
150media_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
178descriptor_t **
179media_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
202char *
203media_get_name(descriptor_t *desc)
204{
205	return (desc->name);
206}
207
208/* ARGSUSED */
209nvlist_t *
210media_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
217int
218media_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 */
242int
243media_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. */
272int
273media_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
289static descriptor_t **
290apply_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. */
348static int
349desc_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
369static int
370get_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
497static int
498get_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 */
537static int
538get_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] != '\0') {
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