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 2005 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 <sys/sunddi.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <unistd.h>
39 #include <sys/dkio.h>
40 
41 #include "libdiskmgt.h"
42 #include "disks_private.h"
43 #include "partition.h"
44 
45 #ifdef sparc
46 #define	les(val)	((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
47 #define	lel(val)	(((unsigned)(les((val)&0x0000FFFF))<<16) | \
48 			    (les((unsigned)((val)&0xffff0000)>>16)))
49 #else
50 #define	les(val)	(val)
51 #define	lel(val)	(val)
52 #endif
53 
54 #define	ISIZE		FD_NUMPART * sizeof (struct ipart)
55 
56 static int	desc_ok(descriptor_t *dp);
57 static int	get_attrs(descriptor_t *dp, struct ipart *iparts,
58 		    nvlist_t *attrs);
59 static int	get_parts(disk_t *disk, struct ipart *iparts, char *opath,
60 		    int opath_len);
61 static int	open_disk(disk_t *diskp, char *opath, int len);
62 static int	has_slices(descriptor_t *desc, int *errp);
63 
64 descriptor_t **
65 partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
66     int *errp)
67 {
68 	if (!desc_ok(desc)) {
69 	    *errp = ENODEV;
70 	    return (NULL);
71 	}
72 
73 	switch (type) {
74 	case DM_MEDIA:
75 	    return (media_get_assocs(desc, errp));
76 	case DM_SLICE:
77 	    if (!has_slices(desc, errp)) {
78 		if (*errp != 0) {
79 		    return (NULL);
80 		}
81 		return (libdiskmgt_empty_desc_array(errp));
82 	    }
83 	    return (slice_get_assocs(desc, errp));
84 	}
85 
86 	*errp = EINVAL;
87 	return (NULL);
88 }
89 
90 /*
91  * This is called by media/slice to get the associated partitions.
92  * For a media desc. we just get all the partitions, but for a slice desc.
93  * we just get the active solaris partition.
94  */
95 descriptor_t **
96 partition_get_assocs(descriptor_t *desc, int *errp)
97 {
98 	descriptor_t	**partitions;
99 	int		pos;
100 	int		i;
101 	struct ipart	iparts[FD_NUMPART];
102 	char		pname[MAXPATHLEN];
103 	int		conv_flag = 0;
104 #if defined(i386) || defined(__amd64)
105 	int		len;
106 #endif
107 
108 	if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) {
109 	    return (libdiskmgt_empty_desc_array(errp));
110 	}
111 
112 	/* allocate the array for the descriptors */
113 	partitions = (descriptor_t **)calloc(FD_NUMPART + 1,
114 	    sizeof (descriptor_t *));
115 	if (partitions == NULL) {
116 	    *errp = ENOMEM;
117 	    return (NULL);
118 	}
119 
120 #if defined(i386) || defined(__amd64)
121 	    /* convert part. name (e.g. c0d0p0) */
122 	    len = strlen(pname);
123 	    if (len > 1 && *(pname + (len - 2)) == 'p') {
124 		conv_flag = 1;
125 		*(pname + (len - 1)) = 0;
126 	    }
127 #endif
128 
129 	/*
130 	 * If this is a slice desc. we need the first active solaris partition
131 	 * and if there isn't one then we need the first solaris partition.
132 	 */
133 	if (desc->type == DM_SLICE) {
134 	    for (i = 0; i < FD_NUMPART; i++) {
135 		if (iparts[i].bootid == ACTIVE &&
136 		    (iparts[i].systid == SUNIXOS ||
137 		    iparts[i].systid == SUNIXOS2)) {
138 			break;
139 		}
140 	    }
141 
142 	    /* no active solaris part., try to get the first solaris part. */
143 	    if (i >= FD_NUMPART) {
144 		for (i = 0; i < FD_NUMPART; i++) {
145 		    if (iparts[i].systid == SUNIXOS ||
146 			iparts[i].systid == SUNIXOS2) {
147 			    break;
148 		    }
149 		}
150 	    }
151 
152 	    if (i < FD_NUMPART) {
153 		/* we found a solaris partition to use */
154 		char	part_name[MAXPATHLEN];
155 
156 		if (conv_flag) {
157 		    /* convert part. name (e.g. c0d0p0) */
158 		    (void) snprintf(part_name, sizeof (part_name), "%s%d",
159 			pname, i);
160 		} else {
161 		    (void) snprintf(part_name, sizeof (part_name), "%d", i);
162 		}
163 
164 		/* the media name comes from the slice desc. */
165 		partitions[0] = cache_get_desc(DM_PARTITION, desc->p.disk,
166 		    part_name, desc->secondary_name, errp);
167 		if (*errp != 0) {
168 		    cache_free_descriptors(partitions);
169 		    return (NULL);
170 		}
171 		partitions[1] = NULL;
172 
173 		return (partitions);
174 
175 	    }
176 
177 	    return (libdiskmgt_empty_desc_array(errp));
178 	}
179 
180 	/* Must be for media, so get all the parts. */
181 
182 	pos = 0;
183 	for (i = 0; i < FD_NUMPART; i++) {
184 	    if (iparts[i].systid != 0) {
185 		char	part_name[MAXPATHLEN];
186 
187 		if (conv_flag) {
188 		    /* convert part. name (e.g. c0d0p0) */
189 		    (void) snprintf(part_name, sizeof (part_name), "%s%d",
190 			pname, i);
191 		} else {
192 		    (void) snprintf(part_name, sizeof (part_name), "%d", i);
193 		}
194 
195 		/* the media name comes from the media desc. */
196 		partitions[pos] = cache_get_desc(DM_PARTITION, desc->p.disk,
197 		    part_name, desc->name, errp);
198 		if (*errp != 0) {
199 		    cache_free_descriptors(partitions);
200 		    return (NULL);
201 		}
202 
203 		pos++;
204 	    }
205 	}
206 	partitions[pos] = NULL;
207 
208 	*errp = 0;
209 	return (partitions);
210 }
211 
212 nvlist_t *
213 partition_get_attributes(descriptor_t *dp, int *errp)
214 {
215 	nvlist_t	*attrs = NULL;
216 	struct ipart	iparts[FD_NUMPART];
217 
218 	if (!desc_ok(dp)) {
219 	    *errp = ENODEV;
220 	    return (NULL);
221 	}
222 
223 	if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) {
224 	    return (NULL);
225 	}
226 
227 	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
228 	    *errp = ENOMEM;
229 	    return (NULL);
230 	}
231 
232 	if ((*errp = get_attrs(dp, iparts, attrs)) != 0) {
233 	    nvlist_free(attrs);
234 	    attrs = NULL;
235 	}
236 
237 	return (attrs);
238 }
239 
240 /*
241  * Look for the partition by the partition number (which is not too useful).
242  */
243 descriptor_t *
244 partition_get_descriptor_by_name(char *name, int *errp)
245 {
246 	descriptor_t	**partitions;
247 	int		i;
248 	descriptor_t	*partition = NULL;
249 
250 	partitions = cache_get_descriptors(DM_PARTITION, errp);
251 	if (*errp != 0) {
252 	    return (NULL);
253 	}
254 
255 	for (i = 0; partitions[i]; i++) {
256 	    if (libdiskmgt_str_eq(name, partitions[i]->name)) {
257 		partition = partitions[i];
258 	    } else {
259 		/* clean up the unused descriptors */
260 		cache_free_descriptor(partitions[i]);
261 	    }
262 	}
263 	free(partitions);
264 
265 	if (partition == NULL) {
266 	    *errp = ENODEV;
267 	}
268 
269 	return (partition);
270 }
271 
272 /* ARGSUSED */
273 descriptor_t **
274 partition_get_descriptors(int filter[], int *errp)
275 {
276 	return (cache_get_descriptors(DM_PARTITION, errp));
277 }
278 
279 char *
280 partition_get_name(descriptor_t *desc)
281 {
282 	return (desc->name);
283 }
284 
285 /* ARGSUSED */
286 nvlist_t *
287 partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
288 {
289 	/* There are no stat types defined for partitions */
290 	*errp = EINVAL;
291 	return (NULL);
292 }
293 
294 /* ARGSUSED */
295 int
296 partition_has_fdisk(disk_t *dp, int fd)
297 {
298 	char		bootsect[512 * 3]; /* 3 sectors to be safe */
299 
300 #ifdef sparc
301 	if (dp->drv_type == DM_DT_FIXED) {
302 	    /* on sparc, only removable media can have fdisk parts. */
303 	    return (0);
304 	}
305 #endif
306 
307 	/*
308 	 * We assume the caller already made sure media was inserted and
309 	 * spun up.
310 	 */
311 
312 	if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
313 	    return (0);
314 	}
315 
316 	return (1);
317 }
318 
319 /*
320  * A partition descriptor points to a disk, the name is the partition number
321  * and the secondary name is the media name.
322  */
323 int
324 partition_make_descriptors()
325 {
326 	int		error;
327 	disk_t		*dp;
328 
329 	dp = cache_get_disklist();
330 	while (dp != NULL) {
331 	    struct ipart	iparts[FD_NUMPART];
332 	    char		pname[MAXPATHLEN];
333 
334 	    if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) {
335 		int	i;
336 		char	mname[MAXPATHLEN];
337 		int	conv_flag = 0;
338 #if defined(i386) || defined(__amd64)
339 		/* convert part. name (e.g. c0d0p0) */
340 		int	len;
341 
342 		len = strlen(pname);
343 		if (len > 1 && *(pname + (len - 2)) == 'p') {
344 		    conv_flag = 1;
345 		    *(pname + (len - 1)) = 0;
346 		}
347 #endif
348 
349 		mname[0] = 0;
350 		(void) media_read_name(dp, mname, sizeof (mname));
351 
352 		for (i = 0; i < FD_NUMPART; i++) {
353 		    if (iparts[i].systid != 0) {
354 			char	part_name[MAXPATHLEN];
355 
356 			if (conv_flag) {
357 			    /* convert part. name (e.g. c0d0p0) */
358 			    (void) snprintf(part_name, sizeof (part_name),
359 				"%s%d", pname, i);
360 			} else {
361 			    (void) snprintf(part_name, sizeof (part_name),
362 				"%d", i);
363 			}
364 
365 			cache_load_desc(DM_PARTITION, dp, part_name, mname,
366 			    &error);
367 			if (error != 0) {
368 			    return (error);
369 			}
370 		    }
371 		}
372 	    }
373 	    dp = dp->next;
374 	}
375 
376 	return (0);
377 }
378 
379 static int
380 get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs)
381 {
382 	char		*p;
383 	int		part_num;
384 
385 	/*
386 	 * We already made sure the media was loaded and ready in the
387 	 * get_parts call within partition_get_attributes.
388 	 */
389 
390 	p = strrchr(dp->name, 'p');
391 	if (p == NULL) {
392 	    p = dp->name;
393 	} else {
394 	    p++;
395 	}
396 	part_num = atoi(p);
397 	if (part_num >= FD_NUMPART || iparts[part_num].systid == 0) {
398 	    return (ENODEV);
399 	}
400 
401 	/* we found the partition */
402 
403 	if (nvlist_add_uint32(attrs, DM_BOOTID,
404 	    (unsigned int)iparts[part_num].bootid) != 0) {
405 	    return (ENOMEM);
406 	}
407 
408 	if (nvlist_add_uint32(attrs, DM_PTYPE,
409 	    (unsigned int)iparts[part_num].systid) != 0) {
410 	    return (ENOMEM);
411 	}
412 
413 	if (nvlist_add_uint32(attrs, DM_BHEAD,
414 	    (unsigned int)iparts[part_num].beghead) != 0) {
415 	    return (ENOMEM);
416 	}
417 
418 	if (nvlist_add_uint32(attrs, DM_BSECT,
419 	    (unsigned int)((iparts[part_num].begsect) & 0x3f)) != 0) {
420 	    return (ENOMEM);
421 	}
422 
423 	if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int)
424 	    ((iparts[part_num].begcyl & 0xff) |
425 	    ((iparts[part_num].begsect & 0xc0) << 2))) != 0) {
426 	    return (ENOMEM);
427 	}
428 
429 	if (nvlist_add_uint32(attrs, DM_EHEAD,
430 	    (unsigned int)iparts[part_num].endhead) != 0) {
431 	    return (ENOMEM);
432 	}
433 
434 	if (nvlist_add_uint32(attrs, DM_ESECT,
435 	    (unsigned int)((iparts[part_num].endsect) & 0x3f)) != 0) {
436 	    return (ENOMEM);
437 	}
438 
439 	if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int)
440 	    ((iparts[part_num].endcyl & 0xff) |
441 	    ((iparts[part_num].endsect & 0xc0) << 2))) != 0) {
442 	    return (ENOMEM);
443 	}
444 
445 	if (nvlist_add_uint32(attrs, DM_RELSECT,
446 	    (unsigned int)iparts[part_num].relsect) != 0) {
447 	    return (ENOMEM);
448 	}
449 
450 	if (nvlist_add_uint32(attrs, DM_NSECTORS,
451 	    (unsigned int)iparts[part_num].numsect) != 0) {
452 	    return (ENOMEM);
453 	}
454 
455 	return (0);
456 }
457 
458 static int
459 get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len)
460 {
461 	int		fd;
462 	struct dk_minfo	minfo;
463 	struct mboot	bootblk;
464 	char		bootsect[512];
465 	int		i;
466 
467 	/* Can't use drive_open_disk since we need the partition dev name. */
468 	if ((fd = open_disk(disk, opath, opath_len)) < 0) {
469 	    return (ENODEV);
470 	}
471 
472 	/* First make sure media is inserted and spun up. */
473 	if (!media_read_info(fd, &minfo)) {
474 	    (void) close(fd);
475 	    return (ENODEV);
476 	}
477 
478 	if (!partition_has_fdisk(disk, fd)) {
479 	    (void) close(fd);
480 	    return (ENOTTY);
481 	}
482 
483 	if (lseek(fd, 0, 0) == -1) {
484 	    (void) close(fd);
485 	    return (ENODEV);
486 	}
487 
488 	if (read(fd, bootsect, 512) != 512) {
489 	    (void) close(fd);
490 	    return (ENODEV);
491 	}
492 	(void) close(fd);
493 
494 	(void) memcpy(&bootblk, bootsect, sizeof (bootblk));
495 
496 	if (les(bootblk.signature) != MBB_MAGIC)  {
497 	    return (ENOTTY);
498 	}
499 
500 	(void) memcpy(iparts, bootblk.parts, ISIZE);
501 
502 	for (i = 0; i < FD_NUMPART; i++) {
503 	    if (iparts[i].systid != 0) {
504 		iparts[i].relsect = lel(iparts[i].relsect);
505 		iparts[i].numsect = lel(iparts[i].numsect);
506 	    }
507 	}
508 
509 	return (0);
510 }
511 /* return 1 if the partition descriptor is still valid, 0 if not. */
512 static int
513 desc_ok(descriptor_t *dp)
514 {
515 	/* First verify the media name for removable media */
516 	if (dp->p.disk->removable) {
517 	    char	mname[MAXPATHLEN];
518 
519 	    if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
520 		return (0);
521 	    }
522 
523 	    if (mname[0] == 0) {
524 		return (libdiskmgt_str_eq(dp->secondary_name, NULL));
525 	    } else {
526 		return (libdiskmgt_str_eq(dp->secondary_name, mname));
527 	    }
528 	}
529 
530 	/*
531 	 * We could verify the partition is still there but this is kind of
532 	 * expensive and other code down the line will do that (e.g. see
533 	 * get_attrs).
534 	 */
535 
536 	return (1);
537 }
538 
539 /*
540  * Return 1 if partition has slices, 0 if not.
541  */
542 static int
543 has_slices(descriptor_t *desc, int *errp)
544 {
545 	int		pnum;
546 	int		i;
547 	char		*p;
548 	struct ipart	iparts[FD_NUMPART];
549 
550 	if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) {
551 	    *errp = ENODEV;
552 	    return (0);
553 	}
554 
555 	p = strrchr(desc->name, 'p');
556 	if (p == NULL) {
557 	    p = desc->name;
558 	} else {
559 	    p++;
560 	}
561 	pnum = atoi(p);
562 
563 	/*
564 	 * Slices are associated with the active solaris partition or if there
565 	 * is no active solaris partition, then the first solaris partition.
566 	 */
567 
568 	*errp = 0;
569 	if (iparts[pnum].bootid == ACTIVE &&
570 	    (iparts[pnum].systid == SUNIXOS ||
571 	    iparts[pnum].systid == SUNIXOS2)) {
572 		return (1);
573 	} else {
574 	    int	active = 0;
575 
576 	    /* Check if there are no active solaris partitions. */
577 	    for (i = 0; i < FD_NUMPART; i++) {
578 		if (iparts[i].bootid == ACTIVE &&
579 		    (iparts[i].systid == SUNIXOS ||
580 		    iparts[i].systid == SUNIXOS2)) {
581 			active = 1;
582 			break;
583 		}
584 	    }
585 
586 	    if (!active) {
587 		/* Check if this is the first solaris partition. */
588 		for (i = 0; i < FD_NUMPART; i++) {
589 		    if (iparts[i].systid == SUNIXOS ||
590 			iparts[i].systid == SUNIXOS2) {
591 			    break;
592 		    }
593 		}
594 
595 		if (i < FD_NUMPART && i == pnum) {
596 		    return (1);
597 		}
598 	    }
599 	}
600 
601 	return (0);
602 }
603 
604 static int
605 open_disk(disk_t *diskp, char *opath, int len)
606 {
607 	char	rmmedia_devpath[MAXPATHLEN];
608 
609 	if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath,
610 	    sizeof (rmmedia_devpath))) {
611 
612 	    int		fd;
613 	    struct stat	buf;
614 
615 	    if (rmmedia_devpath[0] == 0) {
616 		/* removable but no media */
617 		return (-1);
618 	    }
619 
620 	    if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) {
621 		return (-1);
622 	    }
623 
624 	    if (fstat(fd, &buf) != 0) {
625 		(void) close(fd);
626 		return (-1);
627 	    }
628 
629 	    if (buf.st_mode & S_IFCHR) {
630 		/* opened, is device, so done */
631 		if (opath != NULL) {
632 		    (void) strlcpy(opath, rmmedia_devpath, len);
633 		}
634 		return (fd);
635 
636 	    } else if (buf.st_mode & S_IFDIR) {
637 		/* disk w/ slices so handle the directory */
638 		DIR		*dirp;
639 		struct dirent	*dentp;
640 		int		dfd;
641 #ifdef _LP64
642 		struct dirent	*result;
643 #endif
644 
645 		/* each device file in the dir represents a slice */
646 
647 		if ((dirp = fdopendir(fd)) == NULL) {
648 		    (void) close(fd);
649 		    return (-1);
650 		}
651 
652 		if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) +
653 		    PATH_MAX + 1)) == NULL) {
654 		    /* out of memory */
655 		    (void) close(fd);
656 		    return (-1);
657 		}
658 #ifdef _LP64
659 		while (readdir_r(dirp, dentp, &result) != NULL) {
660 #else
661 		while (readdir_r(dirp, dentp) != NULL) {
662 #endif
663 		    char	slice_path[MAXPATHLEN];
664 
665 		    if (libdiskmgt_str_eq(".", dentp->d_name) ||
666 			libdiskmgt_str_eq("..", dentp->d_name)) {
667 			continue;
668 		    }
669 
670 		    (void) snprintf(slice_path, sizeof (slice_path), "%s/%s",
671 			rmmedia_devpath, dentp->d_name);
672 
673 		    if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) {
674 			continue;
675 		    }
676 
677 		    if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) {
678 			/* opened, is device, so done */
679 			free(dentp);
680 			(void) close(fd);
681 			if (opath != NULL) {
682 			    (void) strlcpy(opath, slice_path, len);
683 			}
684 			return (dfd);
685 		    }
686 
687 		    /* not a device, keep looking */
688 		    (void) close(dfd);
689 		}
690 
691 		/* did not find a device under the rmmedia_path */
692 		free(dentp);
693 		(void) close(fd);
694 	    }
695 
696 	    /* didn't find a device under volume management control */
697 	    return (-1);
698 	}
699 
700 	/*
701 	 * Not removable media under volume management control so just open the
702 	 * first devpath.
703 	 */
704 	if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
705 #ifdef sparc
706 	    if (opath != NULL) {
707 		(void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
708 	    }
709 	    return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
710 #else
711 	    /* On intel we need to open partition device (e.g. c0d0p0). */
712 	    char	part_dev[MAXPATHLEN];
713 	    char	*p;
714 
715 	    (void) strlcpy(part_dev, diskp->aliases->devpaths->devpath,
716 		sizeof (part_dev));
717 	    p = strrchr(part_dev, '/');
718 	    if (p == NULL) {
719 		p = strrchr(part_dev, 's');
720 		if (p != NULL) {
721 		    *p = 'p';
722 		}
723 	    } else {
724 		char *ps;
725 
726 		*p = 0;
727 		ps = strrchr((p + 1), 's');
728 		if (ps != NULL) {
729 		    *ps = 'p';
730 		}
731 		*p = '/';
732 	    }
733 
734 	    if (opath != NULL) {
735 		(void) strlcpy(opath, part_dev, len);
736 	    }
737 	    return (open(part_dev, O_RDONLY|O_NDELAY));
738 #endif
739 	}
740 
741 	return (-1);
742 }
743