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