Unleashed v1.4
[unleashed.git] / usr / src / lib / libdiskmgt / common / partition.c
blob28b6400d2b46d73cf2e400eb8be8d1439f678caa
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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>
38 #if defined(i386) || defined(__amd64)
39 #include <sys/dktp/fdisk.h>
40 #include <libfdisk.h>
41 #endif
43 #include "libdiskmgt.h"
44 #include "disks_private.h"
45 #include "partition.h"
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
56 #define TOTAL_NUMPART (FD_NUMPART + MAX_EXT_PARTS)
58 #define ISIZE FD_NUMPART * sizeof (struct ipart)
60 static int desc_ok(descriptor_t *dp);
61 static int get_attrs(descriptor_t *dp, struct ipart *iparts,
62 nvlist_t *attrs);
63 static int get_parts(disk_t *disk, struct ipart *iparts, char *opath,
64 int opath_len);
65 static int open_disk(disk_t *diskp, char *opath, int len);
66 static int has_slices(descriptor_t *desc, int *errp);
68 descriptor_t **
69 partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
70 int *errp)
72 if (!desc_ok(desc)) {
73 *errp = ENODEV;
74 return (NULL);
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);
85 return (libdiskmgt_empty_desc_array(errp));
87 return (slice_get_assocs(desc, errp));
90 *errp = EINVAL;
91 return (NULL);
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.
99 descriptor_t **
100 partition_get_assocs(descriptor_t *desc, int *errp)
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
112 if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) {
113 return (libdiskmgt_empty_desc_array(errp));
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);
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;
131 #endif
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.
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;
147 * no active solaris part.,*try to get the first solaris part.
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;
158 if (i < TOTAL_NUMPART) {
159 /* we found a solaris partition to use */
160 char part_name[MAXPATHLEN];
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);
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);
179 partitions[1] = NULL;
181 return (partitions);
184 return (libdiskmgt_empty_desc_array(errp));
187 /* Must be for media, so get all the parts. */
189 pos = 0;
190 for (i = 0; i < TOTAL_NUMPART; i++) {
191 if (iparts[i].systid != UNUSED) {
192 char part_name[MAXPATHLEN];
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.
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);
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);
218 pos++;
221 partitions[pos] = NULL;
223 *errp = 0;
224 return (partitions);
227 nvlist_t *
228 partition_get_attributes(descriptor_t *dp, int *errp)
230 nvlist_t *attrs = NULL;
231 struct ipart iparts[TOTAL_NUMPART];
233 if (!desc_ok(dp)) {
234 *errp = ENODEV;
235 return (NULL);
238 if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) {
239 return (NULL);
242 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
243 *errp = ENOMEM;
244 return (NULL);
247 if ((*errp = get_attrs(dp, iparts, attrs)) != 0) {
248 nvlist_free(attrs);
249 attrs = NULL;
252 return (attrs);
256 * Look for the partition by the partition number (which is not too useful).
258 descriptor_t *
259 partition_get_descriptor_by_name(char *name, int *errp)
261 descriptor_t **partitions;
262 int i;
263 descriptor_t *partition = NULL;
265 partitions = cache_get_descriptors(DM_PARTITION, errp);
266 if (*errp != 0) {
267 return (NULL);
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]);
278 free(partitions);
280 if (partition == NULL) {
281 *errp = ENODEV;
284 return (partition);
287 /* ARGSUSED */
288 descriptor_t **
289 partition_get_descriptors(int filter[], int *errp)
291 return (cache_get_descriptors(DM_PARTITION, errp));
294 char *
295 partition_get_name(descriptor_t *desc)
297 return (desc->name);
300 /* ARGSUSED */
301 nvlist_t *
302 partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
304 /* There are no stat types defined for partitions */
305 *errp = EINVAL;
306 return (NULL);
309 /* ARGSUSED */
311 partition_has_fdisk(disk_t *dp, int fd)
313 char bootsect[512 * 3]; /* 3 sectors to be safe */
315 #ifdef sparc
316 if (dp->drv_type == DM_DT_FIXED) {
317 /* on sparc, only removable media can have fdisk parts. */
318 return (0);
320 #endif
323 * We assume the caller already made sure media was inserted and
324 * spun up.
327 if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
328 return (0);
331 return (1);
335 * partition_make_descriptors
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.
344 * Parameters: none
346 * Returns: 0 on success
347 * Error value on failure
352 partition_make_descriptors()
354 int error;
355 disk_t *dp;
357 dp = cache_get_disklist();
358 while (dp != NULL) {
359 struct ipart iparts[TOTAL_NUMPART];
360 char pname[MAXPATHLEN];
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;
370 len = strlen(pname);
371 if (len > 1 && *(pname + (len - 2)) == 'p') {
372 conv_flag = 1;
373 *(pname + (len - 1)) = 0;
375 #endif
377 mname[0] = 0;
378 (void) media_read_name(dp, mname, sizeof (mname));
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.
387 for (i = 0; i < TOTAL_NUMPART; i++) {
388 if (iparts[i].systid != UNUSED) {
389 char part_name[MAXPATHLEN];
391 if (conv_flag) {
393 * convert partition name
394 * (e.g. c0d0p1)
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);
405 cache_load_desc(DM_PARTITION, dp,
406 part_name, mname, &error);
407 if (error != 0) {
408 return (error);
413 dp = dp->next;
416 return (0);
419 static int
420 get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs)
422 char *p;
423 int part_num;
426 * We already made sure the media was loaded and ready in the
427 * get_parts call within partition_get_attributes.
430 p = strrchr(dp->name, 'p');
431 if (p == NULL) {
432 p = dp->name;
433 } else {
434 p++;
436 part_num = atoi(p);
437 if (part_num > TOTAL_NUMPART ||
438 iparts[part_num - 1].systid == UNUSED) {
439 return (ENODEV);
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.
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);
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);
459 } else {
460 if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
461 DM_PRIMARY) != 0) {
462 return (ENOMEM);
465 #endif
467 #ifdef sparc
468 if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
469 DM_PRIMARY) != 0) {
470 return (ENOMEM);
472 #endif
475 if (nvlist_add_uint32(attrs, DM_BOOTID,
476 (unsigned int)iparts[part_num - 1].bootid) != 0) {
477 return (ENOMEM);
480 if (nvlist_add_uint32(attrs, DM_PTYPE,
481 (unsigned int)iparts[part_num - 1].systid) != 0) {
482 return (ENOMEM);
485 if (nvlist_add_uint32(attrs, DM_BHEAD,
486 (unsigned int)iparts[part_num - 1].beghead) != 0) {
487 return (ENOMEM);
490 if (nvlist_add_uint32(attrs, DM_BSECT,
491 (unsigned int)((iparts[part_num - 1].begsect) & 0x3f)) != 0) {
492 return (ENOMEM);
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);
501 if (nvlist_add_uint32(attrs, DM_EHEAD,
502 (unsigned int)iparts[part_num - 1].endhead) != 0) {
503 return (ENOMEM);
506 if (nvlist_add_uint32(attrs, DM_ESECT,
507 (unsigned int)((iparts[part_num - 1].endsect) & 0x3f)) != 0) {
508 return (ENOMEM);
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);
517 if (nvlist_add_uint32(attrs, DM_RELSECT,
518 (unsigned int)iparts[part_num - 1].relsect) != 0) {
519 return (ENOMEM);
522 if (nvlist_add_uint32(attrs, DM_NSECTORS,
523 (unsigned int)iparts[part_num - 1].numsect) != 0) {
524 return (ENOMEM);
527 return (0);
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.
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
548 static int
549 get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len)
551 int fd;
552 struct dk_minfo minfo;
553 struct mboot bootblk;
554 char bootsect[512];
555 int i;
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
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);
571 /* First make sure media is inserted and spun up. */
572 if (!media_read_info(fd, &minfo)) {
573 (void) close(fd);
574 return (ENODEV);
577 if (!partition_has_fdisk(disk, fd)) {
578 (void) close(fd);
579 return (ENOTTY);
582 if (lseek(fd, 0, 0) == -1) {
583 (void) close(fd);
584 return (ENODEV);
587 if (read(fd, bootsect, 512) != 512) {
588 (void) close(fd);
589 return (ENODEV);
591 (void) close(fd);
593 (void) memcpy(&bootblk, bootsect, sizeof (bootblk));
595 if (les(bootblk.signature) != MBB_MAGIC) {
596 return (ENOTTY);
600 * Initialize the memory space to clear unknown garbage
601 * that might create confusing results.
603 for (i = 0; i < TOTAL_NUMPART; i++) {
604 (void) memset(&iparts[i], 0, sizeof (struct ipart));
605 iparts[i].systid = UNUSED;
608 (void) memcpy(iparts, bootblk.parts, ISIZE);
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.
616 for (i = 0; i < FD_NUMPART; i++) {
617 if (iparts[i].systid == UNUSED)
618 continue;
620 iparts[i].relsect = lel(iparts[i].relsect);
621 iparts[i].numsect = lel(iparts[i].numsect);
623 #if defined(i386) || defined(__amd64)
624 if (!fdisk_is_dos_extended(iparts[i].systid))
625 continue;
627 len = strlen(disk->aliases->alias) + 1;
628 if ((device = malloc(len)) == NULL) {
629 free(device);
630 continue;
633 (void) snprintf(device, len, "%s", disk->aliases->alias);
635 if ((ret = libfdisk_init(&epp, device, &iparts[i],
636 FDISK_READ_DISK)) != FDISK_SUCCESS) {
638 switch (ret) {
640 * The first 2 error cases indicate that
641 * there is no Solaris logical partition,
642 * which is a valid condition,
643 * so iterating through the disk continues.
644 * Any other error cases indicate there is
645 * a potential problem with the disk, so
646 * don't continue iterating through the disk
647 * and return an error.
649 case FDISK_EBADLOGDRIVE:
650 case FDISK_ENOLOGDRIVE:
651 free(device);
652 libfdisk_fini(&epp);
653 continue;
654 case FDISK_EBADMAGIC:
655 free(device);
656 libfdisk_fini(&epp);
657 return (ENOTTY);
658 default:
659 free(device);
660 libfdisk_fini(&epp);
661 return (ENODEV);
666 * Collect logical drive information
668 for (log_drv = fdisk_get_ld_head(epp), j = FD_NUMPART,
669 tmpsect = 0; (j < TOTAL_NUMPART) && (log_drv != NULL);
670 log_drv = log_drv->next, j++) {
671 iparts[j].bootid = log_drv->parts[0].bootid;
672 iparts[j].beghead = log_drv->parts[0].beghead;
673 iparts[j].begsect = log_drv->parts[0].begsect;
674 iparts[j].begcyl = log_drv->parts[0].begcyl;
675 iparts[j].systid = log_drv->parts[0].systid;
676 iparts[j].endhead = log_drv->parts[0].endhead;
677 iparts[j].endsect = log_drv->parts[0].endsect;
678 iparts[j].endcyl = log_drv->parts[0].endcyl;
679 iparts[j].relsect = (tmpsect +
680 lel(log_drv->parts[0].relsect) + epp->ext_beg_sec);
681 iparts[j].numsect = lel(log_drv->parts[0].numsect);
682 tmpsect = lel(log_drv->parts[1].relsect);
685 /* free the device and the epp memory. */
686 free(device);
687 libfdisk_fini(&epp);
688 #endif
691 return (0);
694 /* return 1 if the partition descriptor is still valid, 0 if not. */
695 static int
696 desc_ok(descriptor_t *dp)
698 /* First verify the media name for removable media */
699 if (dp->p.disk->removable) {
700 char mname[MAXPATHLEN];
702 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
703 return (0);
706 if (mname[0] == 0) {
707 return (libdiskmgt_str_eq(dp->secondary_name, NULL));
708 } else {
709 return (libdiskmgt_str_eq(dp->secondary_name, mname));
714 * We could verify the partition is still there but this is kind of
715 * expensive and other code down the line will do that (e.g. see
716 * get_attrs).
719 return (1);
723 * Return 1 if partition has slices, 0 if not.
725 static int
726 has_slices(descriptor_t *desc, int *errp)
728 int pnum;
729 int i;
730 char *p;
731 struct ipart iparts[TOTAL_NUMPART];
733 if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) {
734 *errp = ENODEV;
735 return (0);
738 p = strrchr(desc->name, 'p');
739 if (p == NULL) {
740 p = desc->name;
741 } else {
742 p++;
744 pnum = atoi(p);
747 * Slices are associated with the active solaris partition or if there
748 * is no active solaris partition, then the first solaris partition.
751 *errp = 0;
752 if (iparts[pnum].bootid == ACTIVE &&
753 (iparts[pnum].systid == SUNIXOS ||
754 iparts[pnum].systid == SUNIXOS2)) {
755 return (1);
756 } else {
757 int active = 0;
759 /* Check if there are no active solaris partitions. */
760 for (i = 0; i < TOTAL_NUMPART; i++) {
761 if (iparts[i].bootid == ACTIVE &&
762 (iparts[i].systid == SUNIXOS ||
763 iparts[i].systid == SUNIXOS2)) {
764 active = 1;
765 break;
769 if (!active) {
770 /* Check if this is the first solaris partition. */
771 for (i = 0; i < TOTAL_NUMPART; i++) {
772 if (iparts[i].systid == SUNIXOS ||
773 iparts[i].systid == SUNIXOS2) {
774 break;
778 if (i < TOTAL_NUMPART && i == pnum) {
779 return (1);
784 return (0);
787 static int
788 open_disk(disk_t *diskp, char *opath, int len)
791 * Just open the first devpath.
793 if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
794 #ifdef sparc
795 if (opath != NULL) {
796 (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
798 return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
799 #else
800 /* On intel we need to open partition device (e.g. c0d0p1). */
801 char part_dev[MAXPATHLEN];
802 char *p;
804 (void) strlcpy(part_dev, diskp->aliases->devpaths->devpath,
805 sizeof (part_dev));
806 p = strrchr(part_dev, '/');
807 if (p == NULL) {
808 p = strrchr(part_dev, 's');
809 if (p != NULL) {
810 *p = 'p';
812 } else {
813 char *ps;
815 *p = 0;
816 ps = strrchr((p + 1), 's');
817 if (ps != NULL) {
818 *ps = 'p';
820 *p = '/';
823 if (opath != NULL) {
824 (void) strlcpy(opath, part_dev, len);
826 return (open(part_dev, O_RDONLY|O_NDELAY));
827 #endif
830 return (-1);