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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <fcntl.h>
27#include <libdevinfo.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/sunddi.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <dirent.h>
35#include <unistd.h>
36#include <sys/dkio.h>
37
38#if defined(i386) || defined(__amd64)
39#include <sys/dktp/fdisk.h>
40#include <libfdisk.h>
41#endif
42
43#include "libdiskmgt.h"
44#include "disks_private.h"
45#include "partition.h"
46
47#ifdef sparc
48#define	les(val)	((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
49#define	lel(val)	(((unsigned)(les((val)&0x0000FFFF))<<16) | \
50			    (les((unsigned)((val)&0xffff0000)>>16)))
51#else
52#define	les(val)	(val)
53#define	lel(val)	(val)
54#endif
55
56#define	TOTAL_NUMPART	(FD_NUMPART + MAX_EXT_PARTS)
57
58#define	ISIZE		FD_NUMPART * sizeof (struct ipart)
59
60static int	desc_ok(descriptor_t *dp);
61static int	get_attrs(descriptor_t *dp, struct ipart *iparts,
62		    nvlist_t *attrs);
63static int	get_parts(disk_t *disk, struct ipart *iparts, char *opath,
64		    int opath_len);
65static int	open_disk(disk_t *diskp, char *opath, int len);
66static int	has_slices(descriptor_t *desc, int *errp);
67
68descriptor_t **
69partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
70    int *errp)
71{
72	if (!desc_ok(desc)) {
73		*errp = ENODEV;
74		return (NULL);
75	}
76
77	switch (type) {
78	case DM_MEDIA:
79		return (media_get_assocs(desc, errp));
80	case DM_SLICE:
81		if (!has_slices(desc, errp)) {
82			if (*errp != 0) {
83				return (NULL);
84			}
85			return (libdiskmgt_empty_desc_array(errp));
86		}
87		return (slice_get_assocs(desc, errp));
88	}
89
90	*errp = EINVAL;
91	return (NULL);
92}
93
94/*
95 * This is called by media/slice to get the associated partitions.
96 * For a media desc. we just get all the partitions, but for a slice desc.
97 * we just get the active solaris partition.
98 */
99descriptor_t **
100partition_get_assocs(descriptor_t *desc, int *errp)
101{
102	descriptor_t	**partitions;
103	int		pos;
104	int		i;
105	struct ipart	iparts[TOTAL_NUMPART];
106	char		pname[MAXPATHLEN];
107	int		conv_flag = 0;
108#if defined(i386) || defined(__amd64)
109	int		len;
110#endif
111
112	if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) {
113		return (libdiskmgt_empty_desc_array(errp));
114	}
115
116	/* allocate the array for the descriptors */
117	partitions = (descriptor_t **)calloc(TOTAL_NUMPART + 1,
118	    sizeof (descriptor_t *));
119	if (partitions == NULL) {
120		*errp = ENOMEM;
121		return (NULL);
122	}
123
124#if defined(i386) || defined(__amd64)
125	/* convert part. name (e.g. c0d0p1) */
126	len = strlen(pname);
127	if (len > 1 && *(pname + (len - 2)) == 'p') {
128		conv_flag = 1;
129		*(pname + (len - 1)) = 0;
130	}
131#endif
132
133	/*
134	 * If this is a slice desc. we need the first active solaris partition
135	 * and if there isn't one then we need the first solaris partition.
136	 */
137	if (desc->type == DM_SLICE) {
138		for (i = 0; i < TOTAL_NUMPART; i++) {
139			if (iparts[i].bootid == ACTIVE &&
140			    (iparts[i].systid == SUNIXOS ||
141			    iparts[i].systid == SUNIXOS2)) {
142				break;
143			}
144		}
145
146		/*
147		 * no active solaris part.,*try to get the first solaris part.
148		 */
149		if (i >= TOTAL_NUMPART) {
150			for (i = 0; i < TOTAL_NUMPART; i++) {
151				if (iparts[i].systid == SUNIXOS ||
152				    iparts[i].systid == SUNIXOS2) {
153					break;
154				}
155			}
156		}
157
158		if (i < TOTAL_NUMPART) {
159		/* we found a solaris partition to use */
160			char	part_name[MAXPATHLEN];
161
162			if (conv_flag) {
163			/* convert part. name (e.g. c0d0p1) */
164				(void) snprintf(part_name, sizeof (part_name),
165				    "%s%d", pname, i+1);
166			} else {
167				(void) snprintf(part_name, sizeof (part_name),
168				    "%d", i+1);
169			}
170
171			/* the media name comes from the slice desc. */
172			partitions[0] = cache_get_desc(DM_PARTITION,
173			    desc->p.disk, part_name, desc->secondary_name,
174			    errp);
175			if (*errp != 0) {
176				cache_free_descriptors(partitions);
177				return (NULL);
178			}
179			partitions[1] = NULL;
180
181			return (partitions);
182		}
183
184		return (libdiskmgt_empty_desc_array(errp));
185	}
186
187	/* Must be for media, so get all the parts. */
188
189	pos = 0;
190	for (i = 0; i < TOTAL_NUMPART; i++) {
191		if (iparts[i].systid != UNUSED) {
192			char	part_name[MAXPATHLEN];
193
194			/*
195			 * Process the descriptors and modify the cxdxpx
196			 * format so that it refers to the fdisk partition
197			 * number and not to the physical disk. This is
198			 * achieved by i+1, where i is the number of the
199			 * physical disk partition.
200			 */
201			if (conv_flag) {
202				/* convert part. name (e.g. c0d0p1) */
203				(void) snprintf(part_name, sizeof (part_name),
204				    "%s%d", pname, i+1);
205			} else {
206				(void) snprintf(part_name, sizeof (part_name),
207				    "%d", i+1);
208			}
209
210			/* the media name comes from the media desc. */
211			partitions[pos] = cache_get_desc(DM_PARTITION,
212			    desc->p.disk, part_name, desc->name, errp);
213			if (*errp != 0) {
214				cache_free_descriptors(partitions);
215				return (NULL);
216			}
217
218			pos++;
219		}
220	}
221	partitions[pos] = NULL;
222
223	*errp = 0;
224	return (partitions);
225}
226
227nvlist_t *
228partition_get_attributes(descriptor_t *dp, int *errp)
229{
230	nvlist_t	*attrs = NULL;
231	struct ipart	iparts[TOTAL_NUMPART];
232
233	if (!desc_ok(dp)) {
234		*errp = ENODEV;
235		return (NULL);
236	}
237
238	if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) {
239		return (NULL);
240	}
241
242	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
243		*errp = ENOMEM;
244		return (NULL);
245	}
246
247	if ((*errp = get_attrs(dp, iparts, attrs)) != 0) {
248		nvlist_free(attrs);
249		attrs = NULL;
250	}
251
252	return (attrs);
253}
254
255/*
256 * Look for the partition by the partition number (which is not too useful).
257 */
258descriptor_t *
259partition_get_descriptor_by_name(char *name, int *errp)
260{
261	descriptor_t	**partitions;
262	int		i;
263	descriptor_t	*partition = NULL;
264
265	partitions = cache_get_descriptors(DM_PARTITION, errp);
266	if (*errp != 0) {
267		return (NULL);
268	}
269
270	for (i = 0; partitions[i]; i++) {
271		if (libdiskmgt_str_eq(name, partitions[i]->name)) {
272			partition = partitions[i];
273		} else {
274			/* clean up the unused descriptors */
275			cache_free_descriptor(partitions[i]);
276		}
277	}
278	free(partitions);
279
280	if (partition == NULL) {
281		*errp = ENODEV;
282	}
283
284	return (partition);
285}
286
287/* ARGSUSED */
288descriptor_t **
289partition_get_descriptors(int filter[], int *errp)
290{
291	return (cache_get_descriptors(DM_PARTITION, errp));
292}
293
294char *
295partition_get_name(descriptor_t *desc)
296{
297	return (desc->name);
298}
299
300/* ARGSUSED */
301nvlist_t *
302partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
303{
304	/* There are no stat types defined for partitions */
305	*errp = EINVAL;
306	return (NULL);
307}
308
309/* ARGSUSED */
310int
311partition_has_fdisk(disk_t *dp, int fd)
312{
313	char		bootsect[512 * 3]; /* 3 sectors to be safe */
314
315#ifdef sparc
316	if (dp->drv_type == DM_DT_FIXED) {
317		/* on sparc, only removable media can have fdisk parts. */
318		return (0);
319	}
320#endif
321
322	/*
323	 * We assume the caller already made sure media was inserted and
324	 * spun up.
325	 */
326
327	if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
328		return (0);
329	}
330
331	return (1);
332}
333
334/*
335 * partition_make_descriptors
336 *
337 * A partition descriptor points to a disk, the name is the partition number
338 * and the secondary name is the media name. The iparts parameter returned
339 * by the get_parts function contains the structures of all of the identified
340 * partitions found on each disk on a system. These are processed into an array
341 * of descriptors. A descriptor contains all of the information about a
342 * specific partition.
343 *
344 * Parameters:  none
345 *
346 * Returns:     0 on success
347 *              Error value on failure
348 *
349 */
350
351int
352partition_make_descriptors()
353{
354	int		error;
355	disk_t		*dp;
356
357	dp = cache_get_disklist();
358	while (dp != NULL) {
359		struct ipart	iparts[TOTAL_NUMPART];
360		char		pname[MAXPATHLEN];
361
362		if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) {
363			int	i;
364			char	mname[MAXPATHLEN];
365			int	conv_flag = 0;
366#if defined(i386) || defined(__amd64)
367			/* convert part. name (e.g. c0d0p1) */
368			int	len;
369
370			len = strlen(pname);
371			if (len > 1 && *(pname + (len - 2)) == 'p') {
372				conv_flag = 1;
373				*(pname + (len - 1)) = 0;
374			}
375#endif
376
377			mname[0] = 0;
378			(void) media_read_name(dp, mname, sizeof (mname));
379
380			/*
381			 * Process the descriptors and modify the cxdxpx
382			 * format so that it refers to the fdisk partition
383			 * number and not to the physical disk. This is
384			 * achieved by i+1, where i is the number of the
385			 * physical disk partition.
386			 */
387			for (i = 0; i < TOTAL_NUMPART; i++) {
388				if (iparts[i].systid != UNUSED) {
389					char    part_name[MAXPATHLEN];
390
391					if (conv_flag) {
392						/*
393						 * convert partition name
394						 * (e.g. c0d0p1)
395						 */
396						(void) snprintf(part_name,
397						    sizeof (part_name),
398						    "%s%d", pname, i+1);
399					} else {
400						(void) snprintf(part_name,
401						    sizeof (part_name),
402						    "%d", i+1);
403					}
404
405					cache_load_desc(DM_PARTITION, dp,
406					    part_name, mname, &error);
407					if (error != 0) {
408						return (error);
409					}
410				}
411			}
412		}
413		dp = dp->next;
414	}
415
416	return (0);
417}
418
419static int
420get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs)
421{
422	char		*p;
423	int		part_num;
424
425	/*
426	 * We already made sure the media was loaded and ready in the
427	 * get_parts call within partition_get_attributes.
428	 */
429
430	p = strrchr(dp->name, 'p');
431	if (p == NULL) {
432		p = dp->name;
433	} else {
434		p++;
435	}
436	part_num = atoi(p);
437	if (part_num > TOTAL_NUMPART ||
438	    iparts[part_num - 1].systid == UNUSED) {
439		return (ENODEV);
440	}
441
442	/*
443	 * A partition has been found. Determine what type of
444	 * partition it is: logical, extended, or primary.
445	 * Collect the information for the partition.
446	 */
447#if defined(i386) || defined(__amd64)
448	if (part_num > FD_NUMPART) {
449		if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
450		    DM_LOGICAL) != 0)  {
451			return (ENOMEM);
452		}
453	} else if (fdisk_is_dos_extended(iparts[part_num - 1].systid)) {
454		if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
455		    DM_EXTENDED) != 0)  {
456			return (ENOMEM);
457		}
458
459	} else {
460		if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
461		    DM_PRIMARY) != 0) {
462			return (ENOMEM);
463		}
464	}
465#endif
466
467#ifdef sparc
468	if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
469	    DM_PRIMARY) != 0) {
470		return (ENOMEM);
471	}
472#endif
473
474
475	if (nvlist_add_uint32(attrs, DM_BOOTID,
476	    (unsigned int)iparts[part_num - 1].bootid) != 0) {
477		return (ENOMEM);
478	}
479
480	if (nvlist_add_uint32(attrs, DM_PTYPE,
481	    (unsigned int)iparts[part_num - 1].systid) != 0) {
482		return (ENOMEM);
483	}
484
485	if (nvlist_add_uint32(attrs, DM_BHEAD,
486	    (unsigned int)iparts[part_num - 1].beghead) != 0) {
487		return (ENOMEM);
488	}
489
490	if (nvlist_add_uint32(attrs, DM_BSECT,
491	    (unsigned int)((iparts[part_num - 1].begsect) & 0x3f)) != 0) {
492		return (ENOMEM);
493	}
494
495	if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int)
496	    ((iparts[part_num - 1].begcyl & 0xff) |
497	    ((iparts[part_num - 1].begsect & 0xc0) << 2))) != 0) {
498		return (ENOMEM);
499	}
500
501	if (nvlist_add_uint32(attrs, DM_EHEAD,
502	    (unsigned int)iparts[part_num - 1].endhead) != 0) {
503		return (ENOMEM);
504	}
505
506	if (nvlist_add_uint32(attrs, DM_ESECT,
507	    (unsigned int)((iparts[part_num - 1].endsect) & 0x3f)) != 0) {
508		return (ENOMEM);
509	}
510
511	if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int)
512	    ((iparts[part_num - 1].endcyl & 0xff) |
513	    ((iparts[part_num - 1].endsect & 0xc0) << 2))) != 0) {
514		return (ENOMEM);
515	}
516
517	if (nvlist_add_uint32(attrs, DM_RELSECT,
518	    (unsigned int)iparts[part_num - 1].relsect) != 0) {
519		return (ENOMEM);
520	}
521
522	if (nvlist_add_uint32(attrs, DM_NSECTORS,
523	    (unsigned int)iparts[part_num - 1].numsect) != 0) {
524		return (ENOMEM);
525	}
526
527	return (0);
528}
529
530/*
531 * get_parts
532 * Discovers the primary, extended, and logical partitions that have
533 * been created on a disk. get_parts loops through the partitions,
534 * collects the information on each partition and stores it in a
535 * partition table.
536 *
537 * Parameters;
538 *		disk		-The disk device to be evaluated for partitions
539 *		iparts		-The structure that holds information about
540 *				 the partitions
541 *		opath		-The device path
542 *		opath_len 	-Buffer size used with opath
543 * Returns:
544 *		0 on Successful completion
545 *		Error Value on failure
546 *
547 */
548static int
549get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len)
550{
551	int		fd;
552	struct dk_minfo	minfo;
553	struct mboot	bootblk;
554	char		bootsect[512];
555	int		i;
556
557#if defined(i386) || defined(__amd64)
558	int 		j, ret;
559	ext_part_t	*epp;		/* extended partition structure */
560	char 		*device;	/* name of fixed disk drive */
561	size_t 		len;
562	logical_drive_t	*log_drv;	/* logical drive structure */
563	uint64_t 	tmpsect;
564#endif
565
566	/* Can't use drive_open_disk since we need the partition dev name. */
567	if ((fd = open_disk(disk, opath, opath_len)) < 0) {
568		return (ENODEV);
569	}
570
571	/* First make sure media is inserted and spun up. */
572	if (!media_read_info(fd, &minfo)) {
573		(void) close(fd);
574		return (ENODEV);
575	}
576
577	if (!partition_has_fdisk(disk, fd)) {
578		(void) close(fd);
579		return (ENOTTY);
580	}
581
582	if (lseek(fd, 0, 0) == -1) {
583		(void) close(fd);
584		return (ENODEV);
585	}
586
587	if (read(fd, bootsect, 512) != 512) {
588		(void) close(fd);
589		return (ENODEV);
590	}
591	(void) close(fd);
592
593	(void) memcpy(&bootblk, bootsect, sizeof (bootblk));
594
595	if (les(bootblk.signature) != MBB_MAGIC)  {
596		return (ENOTTY);
597	}
598
599	/*
600	 * Initialize the memory space to clear unknown garbage
601	 * that might create confusing results.
602	 */
603	for (i = 0;  i < TOTAL_NUMPART; i++) {
604		(void) memset(&iparts[i], 0, sizeof (struct ipart));
605		iparts[i].systid = UNUSED;
606	}
607
608	(void) memcpy(iparts, bootblk.parts, ISIZE);
609
610	/*
611	 * Check to see if a valid partition exists. If a valid partition
612	 * exists, check to see if it is an extended partition.
613	 * If an extended partition exists, collect the logical partition
614	 * data.
615	 */
616	for (i = 0; i < FD_NUMPART; i++) {
617		if (iparts[i].systid == UNUSED)
618			continue;
619
620		iparts[i].relsect = lel(iparts[i].relsect);
621		iparts[i].numsect = lel(iparts[i].numsect);
622
623#if defined(i386) || defined(__amd64)
624		if (!fdisk_is_dos_extended(iparts[i].systid))
625			continue;
626
627		len = strlen(disk->aliases->alias) + 1;
628		if ((device = malloc(len)) == NULL) {
629			if (device)
630				free(device);
631			continue;
632		}
633
634		(void) snprintf(device, len, "%s", disk->aliases->alias);
635
636		if ((ret = libfdisk_init(&epp, device, &iparts[i],
637		    FDISK_READ_DISK)) != FDISK_SUCCESS) {
638
639			switch (ret) {
640				/*
641				 * The first 2 error cases indicate that
642				 * there is no Solaris logical partition,
643				 * which is a valid condition,
644				 * so iterating through the disk continues.
645				 * Any other error cases indicate there is
646				 * a potential problem with the disk, so
647				 * don't continue iterating through the disk
648				 * and return an error.
649				 */
650				case FDISK_EBADLOGDRIVE:
651				case FDISK_ENOLOGDRIVE:
652					free(device);
653					libfdisk_fini(&epp);
654					continue;
655				case FDISK_EBADMAGIC:
656					free(device);
657					libfdisk_fini(&epp);
658					return (ENOTTY);
659				default:
660					free(device);
661					libfdisk_fini(&epp);
662					return (ENODEV);
663			}
664		}
665
666		/*
667		 * Collect logical drive information
668		 */
669		for (log_drv = fdisk_get_ld_head(epp),  j = FD_NUMPART,
670		    tmpsect = 0; (j < TOTAL_NUMPART) && (log_drv != NULL);
671		    log_drv = log_drv->next, j++) {
672			iparts[j].bootid = log_drv->parts[0].bootid;
673			iparts[j].beghead = log_drv->parts[0].beghead;
674			iparts[j].begsect = log_drv->parts[0].begsect;
675			iparts[j].begcyl = log_drv->parts[0].begcyl;
676			iparts[j].systid = log_drv->parts[0].systid;
677			iparts[j].endhead = log_drv->parts[0].endhead;
678			iparts[j].endsect = log_drv->parts[0].endsect;
679			iparts[j].endcyl = log_drv->parts[0].endcyl;
680			iparts[j].relsect = (tmpsect +
681			    lel(log_drv->parts[0].relsect) + epp->ext_beg_sec);
682			iparts[j].numsect = lel(log_drv->parts[0].numsect);
683			tmpsect = lel(log_drv->parts[1].relsect);
684		}
685
686		/* free the device and the epp memory. */
687		free(device);
688		libfdisk_fini(&epp);
689#endif
690	}
691
692	return (0);
693}
694
695/* return 1 if the partition descriptor is still valid, 0 if not. */
696static int
697desc_ok(descriptor_t *dp)
698{
699	/* First verify the media name for removable media */
700	if (dp->p.disk->removable) {
701		char	mname[MAXPATHLEN];
702
703		if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
704			return (0);
705		}
706
707		if (mname[0] == 0) {
708			return (libdiskmgt_str_eq(dp->secondary_name, NULL));
709		} else {
710			return (libdiskmgt_str_eq(dp->secondary_name, mname));
711		}
712	}
713
714	/*
715	 * We could verify the partition is still there but this is kind of
716	 * expensive and other code down the line will do that (e.g. see
717	 * get_attrs).
718	 */
719
720	return (1);
721}
722
723/*
724 * Return 1 if partition has slices, 0 if not.
725 */
726static int
727has_slices(descriptor_t *desc, int *errp)
728{
729	int		pnum;
730	int		i;
731	char		*p;
732	struct ipart	iparts[TOTAL_NUMPART];
733
734	if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) {
735		*errp = ENODEV;
736		return (0);
737	}
738
739	p = strrchr(desc->name, 'p');
740	if (p == NULL) {
741		p = desc->name;
742	} else {
743		p++;
744	}
745	pnum = atoi(p);
746
747	/*
748	 * Slices are associated with the active solaris partition or if there
749	 * is no active solaris partition, then the first solaris partition.
750	 */
751
752	*errp = 0;
753	if (iparts[pnum].bootid == ACTIVE &&
754	    (iparts[pnum].systid == SUNIXOS ||
755	    iparts[pnum].systid == SUNIXOS2)) {
756		return (1);
757	} else {
758		int	active = 0;
759
760		/* Check if there are no active solaris partitions. */
761		for (i = 0; i < TOTAL_NUMPART; i++) {
762			if (iparts[i].bootid == ACTIVE &&
763			    (iparts[i].systid == SUNIXOS ||
764			    iparts[i].systid == SUNIXOS2)) {
765				active = 1;
766				break;
767			}
768		}
769
770		if (!active) {
771			/* Check if this is the first solaris partition. */
772			for (i = 0; i < TOTAL_NUMPART; i++) {
773				if (iparts[i].systid == SUNIXOS ||
774				    iparts[i].systid == SUNIXOS2) {
775					break;
776				}
777			}
778
779			if (i < TOTAL_NUMPART && i == pnum) {
780				return (1);
781			}
782		}
783	}
784
785	return (0);
786}
787
788static int
789open_disk(disk_t *diskp, char *opath, int len)
790{
791	/*
792	 * Just open the first devpath.
793	 */
794	if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
795#ifdef sparc
796	if (opath != NULL) {
797		(void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
798	}
799	return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
800#else
801	/* On intel we need to open partition device (e.g. c0d0p1). */
802	char	part_dev[MAXPATHLEN];
803	char	*p;
804
805	(void) strlcpy(part_dev, diskp->aliases->devpaths->devpath,
806	    sizeof (part_dev));
807	p = strrchr(part_dev, '/');
808	if (p == NULL) {
809		p = strrchr(part_dev, 's');
810		if (p != NULL) {
811			*p = 'p';
812		}
813	} else {
814		char *ps;
815
816		*p = 0;
817		ps = strrchr((p + 1), 's');
818		if (ps != NULL) {
819			*ps = 'p';
820		}
821		*p = '/';
822	}
823
824	if (opath != NULL) {
825		(void) strlcpy(opath, part_dev, len);
826	}
827	return (open(part_dev, O_RDONLY|O_NDELAY));
828#endif
829	}
830
831	return (-1);
832}
833