xref: /illumos-gate/usr/src/lib/libdiskmgt/common/slice.c (revision a07094369b21309434206d9b3601d162693466fc)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <fcntl.h>
30 #include <libdevinfo.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <dirent.h>
35 #include <sys/dkio.h>
36 #include <sys/stat.h>
37 #include <sys/sunddi.h>
38 #include <sys/types.h>
39 #include <sys/vtoc.h>
40 #include <unistd.h>
41 #include <devid.h>
42 #include <dirent.h>
43 #include <sys/dktp/fdisk.h>
44 #include <sys/efi_partition.h>
45 
46 #include "libdiskmgt.h"
47 #include "disks_private.h"
48 #include "partition.h"
49 #ifndef VT_ENOTSUP
50 #define	VT_ENOTSUP	(-5)
51 #endif
52 
53 #define	FMT_UNKNOWN	0
54 #define	FMT_VTOC	1
55 #define	FMT_EFI		2
56 
57 typedef int (*detectorp)(char *, nvlist_t *, int *);
58 
59 static detectorp detectors[] = {
60 	inuse_mnt,
61 	inuse_svm,
62 	inuse_active_zpool,
63 	inuse_lu,
64 	inuse_dump,
65 	inuse_vxvm,
66 	inuse_exported_zpool,
67 	inuse_fs,  /* fs should always be last */
68 	NULL
69 };
70 
71 static int	add_inuse(char *name, nvlist_t *attrs);
72 static int	desc_ok(descriptor_t *dp);
73 static void	dsk2rdsk(char *dsk, char *rdsk, int size);
74 static int	get_attrs(descriptor_t *dp, int fd,  nvlist_t *attrs);
75 static descriptor_t **get_fixed_assocs(descriptor_t *desc, int *errp);
76 static descriptor_t **get_removable_assocs(descriptor_t *desc, char *volm_path,
77 		    int *errp);
78 static int	get_slice_num(slice_t *devp);
79 static int	match_fixed_name(disk_t *dp, char *name, int *errp);
80 static int	match_removable_name(disk_t *dp, char *name, int *errp);
81 static int	make_fixed_descriptors(disk_t *dp);
82 static int	make_removable_descriptors(disk_t *dp);
83 static int	make_volm_dir_descriptors(disk_t *dp, int fd,
84 		    char *volm_path);
85 static int	num_removable_slices(int fd, struct stat *bufp,
86 		    char *volm_path);
87 
88 descriptor_t **
89 slice_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
90     int *errp)
91 {
92 	if (!desc_ok(desc)) {
93 	    *errp = ENODEV;
94 	    return (NULL);
95 	}
96 
97 	switch (type) {
98 	case DM_MEDIA:
99 	    return (media_get_assocs(desc, errp));
100 	case DM_PARTITION:
101 	    return (partition_get_assocs(desc, errp));
102 	}
103 
104 	*errp = EINVAL;
105 	return (NULL);
106 }
107 
108 /*
109  * This is called by media/partition to get the slice descriptors for the given
110  * media/partition descriptor.
111  * For media, just get the slices, but for a partition, it must be a solaris
112  * partition and if there are active partitions, it must be the active one.
113  */
114 descriptor_t **
115 slice_get_assocs(descriptor_t *desc, int *errp)
116 {
117 	int		under_volm = 0;
118 	char		volm_path[MAXPATHLEN];
119 
120 	/* Just check the first drive name. */
121 	if (desc->p.disk->aliases == NULL) {
122 	    *errp = 0;
123 	    return (libdiskmgt_empty_desc_array(errp));
124 	}
125 
126 	if (desc->p.disk->removable) {
127 	    if ((under_volm = media_get_volm_path(desc->p.disk, volm_path,
128 		sizeof (volm_path)))) {
129 		if (volm_path[0] == 0) {
130 		    /* no media */
131 		    *errp = 0;
132 		    return (libdiskmgt_empty_desc_array(errp));
133 		}
134 	    }
135 	}
136 
137 	if (desc->p.disk->removable) {
138 	    if (under_volm) {
139 		return (get_removable_assocs(desc, volm_path, errp));
140 	    } else {
141 		return (get_fixed_assocs(desc, errp));
142 	    }
143 	} else {
144 	    return (get_fixed_assocs(desc, errp));
145 	}
146 }
147 
148 nvlist_t *
149 slice_get_attributes(descriptor_t *dp, int *errp)
150 {
151 	nvlist_t	*attrs = NULL;
152 	int		fd;
153 	char		devpath[MAXPATHLEN];
154 
155 	if (!desc_ok(dp)) {
156 	    *errp = ENODEV;
157 	    return (NULL);
158 	}
159 
160 	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
161 	    *errp = ENOMEM;
162 	    return (NULL);
163 	}
164 
165 	/* dp->name is /dev/dsk, need to convert back to /dev/rdsk */
166 	dsk2rdsk(dp->name, devpath, sizeof (devpath));
167 	fd = open(devpath, O_RDONLY|O_NDELAY);
168 
169 	if ((*errp = get_attrs(dp, fd, attrs)) != 0) {
170 	    nvlist_free(attrs);
171 	    attrs = NULL;
172 	}
173 
174 	if (fd >= 0) {
175 	    (void) close(fd);
176 	}
177 
178 	return (attrs);
179 }
180 
181 /*
182  * Look for the slice by the slice devpath.
183  */
184 descriptor_t *
185 slice_get_descriptor_by_name(char *name, int *errp)
186 {
187 	int		found = 0;
188 	disk_t		*dp;
189 
190 	for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) {
191 	    if (dp->removable) {
192 		found = match_removable_name(dp, name, errp);
193 	    } else {
194 		found = match_fixed_name(dp, name, errp);
195 	    }
196 
197 	    if (found) {
198 		char	mname[MAXPATHLEN];
199 
200 		if (*errp != 0) {
201 		    return (NULL);
202 		}
203 
204 		mname[0] = 0;
205 		(void) media_read_name(dp, mname, sizeof (mname));
206 
207 		return (cache_get_desc(DM_SLICE, dp, name, mname, errp));
208 	    }
209 	}
210 
211 	*errp = ENODEV;
212 	return (NULL);
213 }
214 
215 /* ARGSUSED */
216 descriptor_t **
217 slice_get_descriptors(int filter[], int *errp)
218 {
219 	return (cache_get_descriptors(DM_SLICE, errp));
220 }
221 
222 char *
223 slice_get_name(descriptor_t *desc)
224 {
225 	return (desc->name);
226 }
227 
228 nvlist_t *
229 slice_get_stats(descriptor_t *dp, int stat_type, int *errp)
230 {
231 	nvlist_t	*stats;
232 	char		*str;
233 
234 	if (stat_type != DM_SLICE_STAT_USE) {
235 	    *errp = EINVAL;
236 	    return (NULL);
237 	}
238 
239 	*errp = 0;
240 
241 	if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) {
242 	    *errp = ENOMEM;
243 	    return (NULL);
244 	}
245 
246 	if ((*errp = add_inuse(dp->name, stats)) != 0) {
247 	    return (NULL);
248 	}
249 
250 	/* if no cluster use, check for a use of the local name */
251 	if (nvlist_lookup_string(stats, DM_USED_BY, &str) != 0) {
252 	    disk_t	*diskp;
253 
254 	    diskp = dp->p.disk;
255 	    if (diskp->aliases != NULL && diskp->aliases->cluster) {
256 		slice_t		*sp;
257 		int		snum = -1;
258 		struct dk_minfo	minfo;
259 		struct dk_cinfo	dkinfo;
260 		char		devpath[MAXPATHLEN];
261 		int		fd;
262 
263 		/* dp->name is /dev/dsk, need to convert back to /dev/rdsk */
264 		dsk2rdsk(dp->name, devpath, sizeof (devpath));
265 		fd = open(devpath, O_RDONLY|O_NDELAY);
266 
267 		if (fd >= 0 && media_read_info(fd, &minfo) &&
268 		    ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
269 		    snum = dkinfo.dki_partition;
270 		}
271 
272 		if (fd >= 0) {
273 		    (void) close(fd);
274 		}
275 
276 		if (snum >= 0) {
277 		    for (sp = diskp->aliases->orig_paths; sp != NULL;
278 			sp = sp->next) {
279 
280 			if (sp->slice_num == snum) {
281 			    char	localpath[MAXPATHLEN];
282 
283 			    slice_rdsk2dsk(sp->devpath, localpath,
284 				sizeof (localpath));
285 
286 			    if ((*errp = add_inuse(localpath, stats)) != 0) {
287 				return (NULL);
288 			    }
289 
290 			    break;
291 			}
292 		    }
293 		}
294 	    }
295 	}
296 
297 	return (stats);
298 }
299 
300 /*
301  * A slice descriptor points to a disk, the name is the devpath and the
302  * secondary name is the media name.
303  */
304 int
305 slice_make_descriptors()
306 {
307 	disk_t		*dp;
308 
309 	dp = cache_get_disklist();
310 	while (dp != NULL) {
311 	    int	error;
312 
313 	    if (dp->removable) {
314 		error = make_removable_descriptors(dp);
315 	    } else {
316 		error = make_fixed_descriptors(dp);
317 	    }
318 	    if (error != 0) {
319 		return (error);
320 	    }
321 
322 	    dp = dp->next;
323 	}
324 
325 	return (0);
326 }
327 
328 /* convert rdsk paths to dsk paths */
329 void
330 slice_rdsk2dsk(char *rdsk, char *dsk, int size)
331 {
332 	char	*strp;
333 
334 	(void) strlcpy(dsk, rdsk, size);
335 
336 	if ((strp = strstr(dsk, "/rdsk/")) == NULL) {
337 	    /* not rdsk, check for floppy */
338 	    strp = strstr(dsk, "/rdiskette");
339 	}
340 
341 	if (strp != NULL) {
342 	    strp++;	/* move ptr to the r in rdsk or rdiskette */
343 
344 	    /* move the succeeding chars over by one */
345 	    do {
346 		*strp = *(strp + 1);
347 		strp++;
348 	    } while (*strp);
349 	}
350 }
351 
352 /*
353  * Check if/how the slice is used.
354  */
355 static int
356 add_inuse(char *name, nvlist_t *attrs)
357 {
358 	int	i;
359 	int	error;
360 
361 	for (i = 0; detectors[i] != NULL; i ++) {
362 	    if (detectors[i](name, attrs, &error) || error != 0) {
363 		if (error != 0) {
364 		    return (error);
365 		}
366 		break;
367 	    }
368 	}
369 
370 	return (0);
371 }
372 
373 /* return 1 if the slice descriptor is still valid, 0 if not. */
374 static int
375 desc_ok(descriptor_t *dp)
376 {
377 	/* First verify the media name for removable media */
378 	if (dp->p.disk->removable) {
379 	    char	mname[MAXPATHLEN];
380 
381 	    if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
382 		return (0);
383 	    }
384 
385 	    if (mname[0] == 0) {
386 		return (libdiskmgt_str_eq(dp->secondary_name, NULL));
387 	    } else {
388 		return (libdiskmgt_str_eq(dp->secondary_name, mname));
389 	    }
390 	}
391 
392 	/*
393 	 * We could verify the slice is still there, but other code down the
394 	 * line already does these checks (e.g. see get_attrs).
395 	 */
396 
397 	return (1);
398 }
399 
400 /* convert dsk paths to rdsk paths */
401 static void
402 dsk2rdsk(char *dsk, char *rdsk, int size)
403 {
404 	char	*slashp;
405 	size_t	len;
406 
407 	(void) strlcpy(rdsk, dsk, size);
408 
409 	/* make sure there is enough room to add the r to dsk */
410 	len = strlen(dsk);
411 	if (len + 2 > size) {
412 	    return;
413 	}
414 
415 	if ((slashp = strstr(rdsk, "/dsk/")) == NULL) {
416 	    /* not dsk, check for floppy */
417 	    slashp = strstr(rdsk, "/diskette");
418 	}
419 
420 	if (slashp != NULL) {
421 	    char	*endp;
422 
423 	    endp = rdsk + len;	/* point to terminating 0 */
424 	    /* move the succeeding chars over by one */
425 	    do {
426 		*(endp + 1) = *endp;
427 		endp--;
428 	    } while (endp != slashp);
429 
430 	    *(endp + 1) = 'r';
431 	}
432 }
433 
434 static int
435 get_attrs(descriptor_t *dp, int fd,  nvlist_t *attrs)
436 {
437 	struct dk_minfo	minfo;
438 	int		status;
439 	int		data_format = FMT_UNKNOWN;
440 	int		snum = -1;
441 	int		error;
442 	struct vtoc	vtoc;
443 	struct dk_gpt	*efip;
444 	struct dk_cinfo	dkinfo;
445 	disk_t		*diskp;
446 	char		localpath[MAXPATHLEN];
447 	int		cooked_fd;
448 	struct stat	buf;
449 	int		mntpnt = 0;
450 
451 	if (fd < 0) {
452 	    return (ENODEV);
453 	}
454 
455 	/* First make sure media is inserted and spun up. */
456 	if (!media_read_info(fd, &minfo)) {
457 	    return (ENODEV);
458 	}
459 
460 	if ((status = read_vtoc(fd, &vtoc)) >= 0) {
461 	    data_format = FMT_VTOC;
462 	} else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
463 	    data_format = FMT_EFI;
464 	    if (nvlist_add_boolean(attrs, DM_EFI) != 0) {
465 		efi_free(efip);
466 		return (ENOMEM);
467 	    }
468 	}
469 
470 	if (data_format == FMT_UNKNOWN) {
471 	    return (ENODEV);
472 	}
473 
474 	if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
475 	    snum = dkinfo.dki_partition;
476 	}
477 
478 	/* check the slice */
479 	if (data_format == FMT_VTOC) {
480 	    if (snum < 0 || snum >= vtoc.v_nparts ||
481 		vtoc.v_part[snum].p_size == 0) {
482 		return (ENODEV);
483 	    }
484 	} else { /* data_format == FMT_EFI */
485 	    if (snum < 0 || snum >= efip->efi_nparts ||
486 		efip->efi_parts[snum].p_size == 0) {
487 		efi_free(efip);
488 		return (ENODEV);
489 	    }
490 	}
491 
492 	/* the slice exists */
493 
494 	if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) {
495 	    if (data_format == FMT_EFI) {
496 		efi_free(efip);
497 	    }
498 	    return (ENOMEM);
499 	}
500 
501 	if (data_format == FMT_VTOC) {
502 	    if (nvlist_add_uint64(attrs, DM_START, vtoc.v_part[snum].p_start)
503 		!= 0) {
504 		return (ENOMEM);
505 	    }
506 
507 	    if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size)
508 		!= 0) {
509 		return (ENOMEM);
510 	    }
511 
512 	    if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag)
513 		!= 0) {
514 		return (ENOMEM);
515 	    }
516 
517 	    if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag)
518 		!= 0) {
519 		return (ENOMEM);
520 	    }
521 
522 	} else { /* data_format == FMT_EFI */
523 	    if (nvlist_add_uint64(attrs, DM_START,
524 		efip->efi_parts[snum].p_start) != 0) {
525 		efi_free(efip);
526 		return (ENOMEM);
527 	    }
528 
529 	    if (nvlist_add_uint64(attrs, DM_SIZE, efip->efi_parts[snum].p_size)
530 		!= 0) {
531 		efi_free(efip);
532 		return (ENOMEM);
533 	    }
534 
535 	    if (efip->efi_parts[snum].p_name[0] != 0) {
536 		char	label[EFI_PART_NAME_LEN + 1];
537 
538 		(void) snprintf(label, sizeof (label), "%.*s",
539 		    EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name);
540 		if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) {
541 		    efi_free(efip);
542 		    return (ENOMEM);
543 		}
544 	    }
545 	}
546 
547 	if (data_format == FMT_EFI) {
548 	    efi_free(efip);
549 	}
550 
551 	if (inuse_mnt(dp->name, attrs, &error)) {
552 	    if (error != 0) {
553 		return (error);
554 	    }
555 	    mntpnt = 1;
556 	}
557 
558 	/*
559 	 * Some extra attrs for cluster slices.
560 	 *
561 	 * get localname and possible mnt point for localpath
562 	 */
563 	localpath[0] = 0;
564 	diskp = dp->p.disk;
565 	if (diskp->aliases != NULL && diskp->aliases->cluster) {
566 	    slice_t *sp;
567 
568 	    for (sp = diskp->aliases->orig_paths; sp != NULL; sp = sp->next) {
569 		if (sp->slice_num == -1) {
570 		    /* determine the slice number for this path */
571 		    int			sfd;
572 		    struct dk_cinfo	dkinfo;
573 
574 		    if ((sfd = open(sp->devpath, O_RDONLY|O_NDELAY)) >= 0) {
575 			if (ioctl(sfd, DKIOCINFO, &dkinfo) >= 0) {
576 			    sp->slice_num = dkinfo.dki_partition;
577 			}
578 			(void) close(sfd);
579 		    }
580 		}
581 
582 		if (sp->slice_num == snum) {
583 		    slice_rdsk2dsk(sp->devpath, localpath, sizeof (localpath));
584 
585 		    if (nvlist_add_string(attrs, DM_LOCALNAME, localpath)
586 			!= 0) {
587 			return (ENOMEM);
588 		    }
589 
590 		    if (mntpnt == 0) {
591 			if (inuse_mnt(localpath, attrs, &error)) {
592 			    if (error != 0) {
593 				return (error);
594 			    }
595 			}
596 		    }
597 
598 		    break;
599 		}
600 	    }
601 	}
602 
603 	if (fstat(fd, &buf) != -1) {
604 	    if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) {
605 		return (ENOMEM);
606 	    }
607 	}
608 
609 	/*
610 	 * We need to open the cooked slice (not the raw one) to get the
611 	 * correct devid.  Also see if we need to read the localpath for the
612 	 * cluster disk, since the minor name is unavailable for the did pseudo
613 	 * device.
614 	 */
615 	if (localpath[0] != 0) {
616 	    cooked_fd = open(localpath, O_RDONLY|O_NDELAY);
617 	} else {
618 	    cooked_fd = open(dp->name, O_RDONLY|O_NDELAY);
619 	}
620 
621 	if (cooked_fd >= 0) {
622 	    int		no_mem = 0;
623 	    ddi_devid_t	devid;
624 
625 	    if (devid_get(cooked_fd, &devid) == 0) {
626 		char	*minor;
627 
628 		if (devid_get_minor_name(cooked_fd, &minor) == 0) {
629 		    char	*devidstr;
630 
631 		    if ((devidstr = devid_str_encode(devid, minor)) != 0) {
632 
633 			if (nvlist_add_string(attrs, DM_DEVICEID, devidstr)
634 			    != 0) {
635 			    no_mem = 1;
636 			}
637 
638 			devid_str_free(devidstr);
639 		    }
640 		    devid_str_free(minor);
641 		}
642 		devid_free(devid);
643 	    }
644 	    (void) close(cooked_fd);
645 
646 	    if (no_mem) {
647 		return (ENOMEM);
648 	    }
649 	}
650 
651 	return (0);
652 }
653 
654 static descriptor_t **
655 get_fixed_assocs(descriptor_t *desc, int *errp)
656 {
657 	int		fd;
658 	int		status;
659 	int		data_format = FMT_UNKNOWN;
660 	int		cnt;
661 	struct vtoc	vtoc;
662 	struct dk_gpt	*efip;
663 	int		pos;
664 	char		*media_name = NULL;
665 	slice_t		*devp;
666 	descriptor_t	**slices;
667 
668 	if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) {
669 	    *errp = ENODEV;
670 	    return (NULL);
671 	}
672 
673 	if ((status = read_vtoc(fd, &vtoc)) >= 0) {
674 	    data_format = FMT_VTOC;
675 	} else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
676 	    data_format = FMT_EFI;
677 	} else {
678 	    (void) close(fd);
679 	    *errp = 0;
680 	    return (libdiskmgt_empty_desc_array(errp));
681 	}
682 	(void) close(fd);
683 
684 	/* count the number of slices */
685 	for (cnt = 0, devp = desc->p.disk->aliases->devpaths; devp != NULL;
686 	    devp = devp->next, cnt++);
687 
688 	/* allocate the array for the descriptors */
689 	slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
690 	if (slices == NULL) {
691 	    if (data_format == FMT_EFI) {
692 		efi_free(efip);
693 	    }
694 	    *errp = ENOMEM;
695 	    return (NULL);
696 	}
697 
698 	/* get the media name from the descriptor */
699 	if (desc->type == DM_MEDIA) {
700 	    media_name = desc->name;
701 	} else {
702 	    /* must be a DM_PARTITION */
703 	    media_name = desc->secondary_name;
704 	}
705 
706 	pos = 0;
707 	for (devp = desc->p.disk->aliases->devpaths; devp != NULL;
708 	    devp = devp->next) {
709 
710 	    int		slice_num;
711 	    char	devpath[MAXPATHLEN];
712 
713 	    slice_num = get_slice_num(devp);
714 	    /* can't get slicenum, so no need to keep trying the drive */
715 	    if (slice_num == -1) {
716 		break;
717 	    }
718 
719 	    if (data_format == FMT_VTOC) {
720 		if (slice_num >= vtoc.v_nparts ||
721 		    vtoc.v_part[slice_num].p_size == 0) {
722 		    continue;
723 		}
724 	    } else { /* data_format == FMT_EFI */
725 		if (slice_num >= efip->efi_nparts ||
726 		    efip->efi_parts[slice_num].p_size == 0) {
727 		    continue;
728 		}
729 	    }
730 
731 	    slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
732 	    slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath,
733 		media_name, errp);
734 	    if (*errp != 0) {
735 		cache_free_descriptors(slices);
736 		if (data_format == FMT_EFI) {
737 		    efi_free(efip);
738 		}
739 		return (NULL);
740 	    }
741 	    pos++;
742 	}
743 	slices[pos] = NULL;
744 
745 	if (data_format == FMT_EFI) {
746 	    efi_free(efip);
747 	}
748 
749 	*errp = 0;
750 	return (slices);
751 }
752 
753 /*
754  * Called for loaded removable media under volume management control.
755  */
756 static descriptor_t **
757 get_removable_assocs(descriptor_t *desc, char *volm_path, int *errp)
758 {
759 	int		pos;
760 	int		fd;
761 	int		cnt;
762 	struct stat	buf;
763 	descriptor_t	**slices;
764 	char		*media_name = NULL;
765 	char		devpath[MAXPATHLEN];
766 
767 	/* get the media name from the descriptor */
768 	if (desc->type == DM_MEDIA) {
769 		media_name = desc->name;
770 	} else {
771 		/* must be a DM_PARTITION */
772 		media_name = desc->secondary_name;
773 	}
774 
775 	/*
776 	 * For removable media under volm control the volm_path will
777 	 * either be a device (if the media is made up of a single slice) or
778 	 * a directory (if the media has multiple slices) with the slices
779 	 * as devices contained in the directory.
780 	 */
781 
782 	if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) < 0 ||
783 	    fstat(fd, &buf) != 0) {
784 		*errp = ENODEV;
785 		return (NULL);
786 	}
787 
788 	cnt = num_removable_slices(fd, &buf, volm_path);
789 
790 	/* allocate the array for the descriptors */
791 	slices = calloc(cnt + 1, sizeof (descriptor_t *));
792 	if (slices == NULL) {
793 		*errp = ENOMEM;
794 		return (NULL);
795 	}
796 
797 	slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
798 
799 	pos = 0;
800 	*errp = 0;
801 	if (S_ISCHR(buf.st_mode)) {
802 		struct dk_minfo	minfo;
803 
804 		/* Make sure media has readable label */
805 		if (media_read_info(fd, &minfo)) {
806 			int		status;
807 			int		data_format = FMT_UNKNOWN;
808 			struct vtoc	vtoc;
809 			struct dk_gpt	*efip;
810 
811 			if ((status = read_vtoc(fd, &vtoc)) >= 0) {
812 				data_format = FMT_VTOC;
813 			} else if (status == VT_ENOTSUP &&
814 			    efi_alloc_and_read(fd, &efip) >= 0) {
815 				data_format = FMT_EFI;
816 			}
817 
818 			if (data_format != FMT_UNKNOWN) {
819 			    /* has a readable label */
820 				slices[pos++] =
821 				    cache_get_desc(DM_SLICE, desc->p.disk,
822 				    devpath, media_name, errp);
823 			}
824 		}
825 		(void) close(fd);
826 	} else if (S_ISDIR(buf.st_mode)) {
827 		DIR		*dirp;
828 		struct dirent	*dentp;
829 
830 		/* rewind, num_removable_slices already traversed */
831 		(void) lseek(fd, 0, SEEK_SET);
832 
833 		if ((dirp = fdopendir(fd)) == NULL) {
834 			*errp = errno;
835 			(void) close(fd);
836 			return (NULL);
837 		}
838 
839 		while ((dentp = readdir(dirp)) != NULL) {
840 			int	dfd;
841 			int	is_dev = 0;
842 			char	slice_path[MAXPATHLEN];
843 
844 			if (libdiskmgt_str_eq(".", dentp->d_name) ||
845 			    libdiskmgt_str_eq("..", dentp->d_name)) {
846 				continue;
847 			}
848 
849 			(void) snprintf(slice_path, sizeof (slice_path),
850 			    "%s/%s", devpath, dentp->d_name);
851 
852 			if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) {
853 				struct stat	buf;
854 
855 				if (fstat(dfd, &buf) == 0 &&
856 				    S_ISCHR(buf.st_mode)) {
857 					is_dev = 1;
858 				}
859 				(void) close(dfd);
860 			}
861 
862 			if (!is_dev) {
863 				continue;
864 			}
865 
866 			slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk,
867 			    slice_path, media_name, errp);
868 			if (*errp != 0) {
869 				break;
870 			}
871 		}
872 		(void) closedir(dirp);
873 	} else {
874 		(void) close(fd);
875 	}
876 
877 	slices[pos] = NULL;
878 
879 	if (*errp != 0) {
880 		cache_free_descriptors(slices);
881 		return (NULL);
882 	}
883 
884 	return (slices);
885 }
886 
887 static int
888 get_slice_num(slice_t *devp)
889 {
890 	/* check if we already determined the devpath slice number */
891 	if (devp->slice_num == -1) {
892 	    int		fd;
893 
894 	    if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) {
895 		struct dk_cinfo	dkinfo;
896 		if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
897 		    devp->slice_num = dkinfo.dki_partition;
898 		}
899 		(void) close(fd);
900 	    }
901 	}
902 
903 	return (devp->slice_num);
904 }
905 
906 static int
907 make_fixed_descriptors(disk_t *dp)
908 {
909 	int		error = 0;
910 	alias_t		*ap;
911 	slice_t		*devp;
912 	char		mname[MAXPATHLEN];
913 	int		data_format = FMT_UNKNOWN;
914 	struct vtoc	vtoc;
915 	struct dk_gpt	*efip;
916 
917 	/* Just check the first drive name. */
918 	if ((ap = dp->aliases) == NULL) {
919 	    return (0);
920 	}
921 
922 	mname[0] = 0;
923 	(void) media_read_name(dp, mname, sizeof (mname));
924 
925 	for (devp = ap->devpaths; devp != NULL; devp = devp->next) {
926 	    int		slice_num;
927 	    char	devpath[MAXPATHLEN];
928 
929 	    slice_num = get_slice_num(devp);
930 	    /* can't get slicenum, so no need to keep trying the drive */
931 	    if (slice_num == -1) {
932 		break;
933 	    }
934 
935 	    if (data_format == FMT_UNKNOWN) {
936 		int	fd;
937 		int	status;
938 
939 		if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) {
940 		    if ((status = read_vtoc(fd, &vtoc)) >= 0) {
941 			data_format = FMT_VTOC;
942 		    } else if (status == VT_ENOTSUP &&
943 			efi_alloc_and_read(fd, &efip) >= 0) {
944 			data_format = FMT_EFI;
945 		    }
946 		    (void) close(fd);
947 		}
948 	    }
949 
950 	    /* can't get slice data, so no need to keep trying the drive */
951 	    if (data_format == FMT_UNKNOWN) {
952 		break;
953 	    }
954 
955 	    if (data_format == FMT_VTOC) {
956 		if (slice_num >= vtoc.v_nparts ||
957 		    vtoc.v_part[slice_num].p_size == 0) {
958 		    continue;
959 		}
960 	    } else { /* data_format == FMT_EFI */
961 		if (slice_num >= efip->efi_nparts ||
962 		    efip->efi_parts[slice_num].p_size == 0) {
963 		    continue;
964 		}
965 	    }
966 
967 	    slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
968 	    cache_load_desc(DM_SLICE, dp, devpath, mname, &error);
969 	    if (error != 0) {
970 		break;
971 	    }
972 	}
973 
974 	if (data_format == FMT_EFI) {
975 	    efi_free(efip);
976 	}
977 
978 	return (error);
979 }
980 
981 /*
982  * For removable media under volm control we have to do some special handling.
983  * We don't use the vtoc and /dev/dsk devpaths, since the slices are named
984  * under the /vol fs.
985  */
986 static int
987 make_removable_descriptors(disk_t *dp)
988 {
989 	char		volm_path[MAXPATHLEN];
990 	int		error;
991 	int		fd;
992 
993 	/*
994 	 * If this removable drive is not under volm control, just use
995 	 * normal handling.
996 	 */
997 	if (!media_get_volm_path(dp, volm_path, sizeof (volm_path))) {
998 	    return (make_fixed_descriptors(dp));
999 	}
1000 
1001 	if (volm_path[0] == 0) {
1002 	    /* no media */
1003 	    return (0);
1004 	}
1005 
1006 	/*
1007 	 * For removable media under volm control the rmmedia_devapth will
1008 	 * either be a device (if the media is made up of a single slice) or
1009 	 * a directory (if the media has multiple slices) with the slices
1010 	 * as devices contained in the directory.
1011 	 */
1012 	error = 0;
1013 	if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0) {
1014 	    struct stat	buf;
1015 
1016 	    if (fstat(fd, &buf) == 0) {
1017 		if (S_ISCHR(buf.st_mode)) {
1018 		    int			status;
1019 		    int			data_format = FMT_UNKNOWN;
1020 		    struct dk_minfo	minfo;
1021 		    int			error;
1022 		    struct vtoc		vtoc;
1023 		    struct dk_gpt	*efip;
1024 		    char		devpath[MAXPATHLEN];
1025 
1026 		    /* Make sure media has readable label */
1027 		    if (!media_read_info(fd, &minfo)) {
1028 			/* no media */
1029 			return (0);
1030 		    }
1031 
1032 		    if ((status = read_vtoc(fd, &vtoc)) >= 0) {
1033 			data_format = FMT_VTOC;
1034 		    } else if (status == VT_ENOTSUP &&
1035 			efi_alloc_and_read(fd, &efip) >= 0) {
1036 			data_format = FMT_EFI;
1037 		    }
1038 
1039 		    if (data_format == FMT_UNKNOWN) {
1040 			/* no readable label */
1041 			return (0);
1042 		    }
1043 
1044 		    slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
1045 		    /* The media name is the volm_path in this case. */
1046 		    cache_load_desc(DM_SLICE, dp, devpath, volm_path, &error);
1047 
1048 		} else if (S_ISDIR(buf.st_mode)) {
1049 		    /* each device file in the dir represents a slice */
1050 		    error = make_volm_dir_descriptors(dp, fd, volm_path);
1051 		}
1052 	    }
1053 	    (void) close(fd);
1054 	}
1055 
1056 	return (error);
1057 }
1058 
1059 /*
1060  * This handles removable media with slices under volume management control.
1061  * In this case we have a dir which is the media name and each slice on the
1062  * media is a device file in this dir.
1063  */
1064 static int
1065 make_volm_dir_descriptors(disk_t *dp, int dirfd, char *volm_path)
1066 {
1067 	int		error;
1068 	DIR		*dirp;
1069 	struct dirent	*dentp;
1070 	char		devpath[MAXPATHLEN];
1071 
1072 	dirfd = dup(dirfd);
1073 	if (dirfd < 0)
1074 		return (0);
1075 	if ((dirp = fdopendir(dirfd)) == NULL) {
1076 		(void) close(dirfd);
1077 		return (0);
1078 	}
1079 
1080 	slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
1081 
1082 	error = 0;
1083 	while ((dentp = readdir(dirp)) != NULL) {
1084 		int	fd;
1085 		char	slice_path[MAXPATHLEN];
1086 
1087 		if (libdiskmgt_str_eq(".", dentp->d_name) ||
1088 		    libdiskmgt_str_eq("..", dentp->d_name)) {
1089 			continue;
1090 		}
1091 
1092 		(void) snprintf(slice_path, sizeof (slice_path), "%s/%s",
1093 		    devpath, dentp->d_name);
1094 
1095 		if ((fd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) {
1096 			struct stat	buf;
1097 
1098 			/* The media name is the volm_path in this case. */
1099 			if (fstat(fd, &buf) == 0 && S_ISCHR(buf.st_mode)) {
1100 				cache_load_desc(DM_SLICE, dp, slice_path,
1101 				    volm_path, &error);
1102 				if (error != 0) {
1103 					(void) close(fd);
1104 					break;
1105 				}
1106 			}
1107 
1108 			(void) close(fd);
1109 		}
1110 	}
1111 	(void) closedir(dirp);
1112 
1113 	return (error);
1114 }
1115 
1116 /*
1117  * Just look for the name on the devpaths we have cached. Return 1 if we
1118  * find the name and the size of that slice is non-zero.
1119  */
1120 static int
1121 match_fixed_name(disk_t *diskp, char *name, int *errp)
1122 {
1123 	slice_t		*dp = NULL;
1124 	alias_t		*ap;
1125 	int		slice_num;
1126 	int		fd;
1127 	int		status;
1128 	int		data_format = FMT_UNKNOWN;
1129 	struct vtoc	vtoc;
1130 	struct dk_gpt	*efip;
1131 
1132 	ap = diskp->aliases;
1133 	while (ap != NULL) {
1134 	    slice_t	*devp;
1135 
1136 	    devp = ap->devpaths;
1137 	    while (devp != NULL) {
1138 		char	path[MAXPATHLEN];
1139 
1140 		slice_rdsk2dsk(devp->devpath, path, sizeof (path));
1141 		if (libdiskmgt_str_eq(path, name)) {
1142 		    /* found it */
1143 		    dp = devp;
1144 		    break;
1145 		}
1146 
1147 		devp = devp->next;
1148 	    }
1149 
1150 	    if (dp != NULL) {
1151 		break;
1152 	    }
1153 
1154 	    ap = ap->next;
1155 	}
1156 
1157 	if (dp == NULL) {
1158 	    *errp = 0;
1159 	    return (0);
1160 	}
1161 
1162 	/*
1163 	 * If we found a match on the name we now have to check that this
1164 	 * slice really exists (non-0 size).
1165 	 */
1166 
1167 	slice_num = get_slice_num(dp);
1168 	/* can't get slicenum, so no slice */
1169 	if (slice_num == -1) {
1170 	    *errp = ENODEV;
1171 	    return (1);
1172 	}
1173 
1174 	if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) {
1175 	    *errp = ENODEV;
1176 	    return (1);
1177 	}
1178 
1179 	if ((status = read_vtoc(fd, &vtoc)) >= 0) {
1180 	    data_format = FMT_VTOC;
1181 	} else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
1182 	    data_format = FMT_EFI;
1183 	} else {
1184 	    (void) close(fd);
1185 	    *errp = ENODEV;
1186 	    return (1);
1187 	}
1188 	(void) close(fd);
1189 
1190 	if (data_format == FMT_VTOC) {
1191 	    if (slice_num < vtoc.v_nparts &&
1192 		vtoc.v_part[slice_num].p_size > 0) {
1193 		*errp = 0;
1194 		return (1);
1195 	    }
1196 	} else { /* data_format == FMT_EFI */
1197 	    if (slice_num < efip->efi_nparts &&
1198 		efip->efi_parts[slice_num].p_size > 0) {
1199 		efi_free(efip);
1200 		*errp = 0;
1201 		return (1);
1202 	    }
1203 	    efi_free(efip);
1204 	}
1205 
1206 	*errp = ENODEV;
1207 	return (1);
1208 }
1209 
1210 static int
1211 match_removable_name(disk_t *diskp, char *name, int *errp)
1212 {
1213 	char		volm_path[MAXPATHLEN];
1214 	int		found;
1215 	int		fd;
1216 	struct stat	buf;
1217 
1218 	/*
1219 	 * If this removable drive is not under volm control, just use
1220 	 * normal handling.
1221 	 */
1222 	if (!media_get_volm_path(diskp, volm_path, sizeof (volm_path))) {
1223 	    return (match_fixed_name(diskp, name, errp));
1224 	}
1225 
1226 	if (volm_path[0] == 0) {
1227 	    /* no media */
1228 	    *errp = 0;
1229 	    return (0);
1230 	}
1231 
1232 	/*
1233 	 * For removable media under volm control the rmmedia_devapth will
1234 	 * either be a device (if the media is made up of a single slice) or
1235 	 * a directory (if the media has multiple slices) with the slices
1236 	 * as devices contained in the directory.
1237 	 */
1238 
1239 	*errp = 0;
1240 
1241 	if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) == -1 ||
1242 	    fstat(fd, &buf) != 0) {
1243 		return (0);
1244 	}
1245 
1246 	found = 0;
1247 
1248 	if (S_ISCHR(buf.st_mode)) {
1249 		char	devpath[MAXPATHLEN];
1250 
1251 		slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
1252 		if (libdiskmgt_str_eq(name, devpath)) {
1253 			found = 1;
1254 		}
1255 		(void) close(fd);
1256 		return (found);
1257 	} else if (S_ISDIR(buf.st_mode)) {
1258 		/* each device file in the dir represents a slice */
1259 		DIR		*dirp;
1260 		struct dirent	*dentp;
1261 		char		devpath[MAXPATHLEN];
1262 
1263 		if ((dirp = fdopendir(fd)) == NULL) {
1264 			(void) close(fd);
1265 			return (0);
1266 		}
1267 
1268 		slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
1269 
1270 		while ((dentp = readdir(dirp)) != NULL) {
1271 			char	slice_path[MAXPATHLEN];
1272 
1273 			if (libdiskmgt_str_eq(".", dentp->d_name) ||
1274 			    libdiskmgt_str_eq("..", dentp->d_name)) {
1275 				continue;
1276 			}
1277 
1278 			(void) snprintf(slice_path, sizeof (slice_path),
1279 			    "%s/%s", devpath, dentp->d_name);
1280 
1281 			if (libdiskmgt_str_eq(name, slice_path)) {
1282 				/* found name, check device */
1283 				int	dfd;
1284 				int	is_dev = 0;
1285 
1286 				dfd = open(slice_path, O_RDONLY|O_NDELAY);
1287 				if (dfd >= 0) {
1288 					struct stat	buf;
1289 
1290 					if (fstat(dfd, &buf) == 0 &&
1291 					    S_ISCHR(buf.st_mode)) {
1292 						is_dev = 1;
1293 					}
1294 					(void) close(dfd);
1295 				}
1296 
1297 				/* we found the name */
1298 				found = 1;
1299 
1300 				if (!is_dev) {
1301 					*errp = ENODEV;
1302 				}
1303 
1304 				break;
1305 			}
1306 		}
1307 		(void) closedir(dirp);
1308 	} else {
1309 		(void) close(fd);
1310 	}
1311 
1312 	return (found);
1313 }
1314 
1315 static int
1316 num_removable_slices(int fd, struct stat *bufp, char *volm_path)
1317 {
1318 	int cnt = 0;
1319 
1320 	if (S_ISCHR(bufp->st_mode))
1321 		return (1);
1322 
1323 	if (S_ISDIR(bufp->st_mode)) {
1324 		/* each device file in the dir represents a slice */
1325 		DIR		*dirp;
1326 		struct dirent	*dentp;
1327 		char		devpath[MAXPATHLEN];
1328 
1329 		fd = dup(fd);
1330 
1331 		if (fd < 0)
1332 			return (0);
1333 
1334 		if ((dirp = fdopendir(fd)) == NULL) {
1335 			(void) close(fd);
1336 			return (0);
1337 		}
1338 
1339 		slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
1340 
1341 		while ((dentp = readdir(dirp)) != NULL) {
1342 			int	dfd;
1343 			char	slice_path[MAXPATHLEN];
1344 
1345 			if (libdiskmgt_str_eq(".", dentp->d_name) ||
1346 			    libdiskmgt_str_eq("..", dentp->d_name)) {
1347 				continue;
1348 			}
1349 
1350 			(void) snprintf(slice_path, sizeof (slice_path),
1351 			    "%s/%s", devpath, dentp->d_name);
1352 
1353 			if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) {
1354 				struct stat	buf;
1355 
1356 				if (fstat(dfd, &buf) == 0 &&
1357 				    S_ISCHR(buf.st_mode)) {
1358 					cnt++;
1359 				}
1360 				(void) close(dfd);
1361 			}
1362 		}
1363 		(void) closedir(dirp);
1364 	}
1365 	return (cnt);
1366 }
1367