/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <fcntl.h>
#include <libdevinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sunddi.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/dkio.h>
#if defined(i386) || defined(__amd64)
#include <sys/dktp/fdisk.h>
#include <libfdisk.h>
#endif
#include "libdiskmgt.h"
#include "disks_private.h"
#include "partition.h"
#ifdef sparc
#define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
#define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \
(les((unsigned)((val)&0xffff0000)>>16)))
#else
#define les(val) (val)
#define lel(val) (val)
#endif
#define TOTAL_NUMPART (FD_NUMPART + MAX_EXT_PARTS)
#define ISIZE FD_NUMPART * sizeof (struct ipart)
static int desc_ok(descriptor_t *dp);
static int get_attrs(descriptor_t *dp, struct ipart *iparts,
nvlist_t *attrs);
static int get_parts(disk_t *disk, struct ipart *iparts, char *opath,
int opath_len);
static int open_disk(disk_t *diskp, char *opath, int len);
static int has_slices(descriptor_t *desc, int *errp);
descriptor_t **
partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
int *errp)
{
if (!desc_ok(desc)) {
*errp = ENODEV;
return (NULL);
}
switch (type) {
case DM_MEDIA:
return (media_get_assocs(desc, errp));
case DM_SLICE:
if (!has_slices(desc, errp)) {
if (*errp != 0) {
return (NULL);
}
return (libdiskmgt_empty_desc_array(errp));
}
return (slice_get_assocs(desc, errp));
}
*errp = EINVAL;
return (NULL);
}
/*
* This is called by media/slice to get the associated partitions.
* For a media desc. we just get all the partitions, but for a slice desc.
* we just get the active solaris partition.
*/
descriptor_t **
partition_get_assocs(descriptor_t *desc, int *errp)
{
descriptor_t **partitions;
int pos;
int i;
struct ipart iparts[TOTAL_NUMPART];
char pname[MAXPATHLEN];
int conv_flag = 0;
#if defined(i386) || defined(__amd64)
int len;
#endif
if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) {
return (libdiskmgt_empty_desc_array(errp));
}
/* allocate the array for the descriptors */
partitions = (descriptor_t **)calloc(TOTAL_NUMPART + 1,
sizeof (descriptor_t *));
if (partitions == NULL) {
*errp = ENOMEM;
return (NULL);
}
#if defined(i386) || defined(__amd64)
/* convert part. name (e.g. c0d0p1) */
len = strlen(pname);
if (len > 1 && *(pname + (len - 2)) == 'p') {
conv_flag = 1;
*(pname + (len - 1)) = 0;
}
#endif
/*
* If this is a slice desc. we need the first active solaris partition
* and if there isn't one then we need the first solaris partition.
*/
if (desc->type == DM_SLICE) {
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].bootid == ACTIVE &&
(iparts[i].systid == SUNIXOS ||
iparts[i].systid == SUNIXOS2)) {
break;
}
}
/*
* no active solaris part.,*try to get the first solaris part.
*/
if (i >= TOTAL_NUMPART) {
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].systid == SUNIXOS ||
iparts[i].systid == SUNIXOS2) {
break;
}
}
}
if (i < TOTAL_NUMPART) {
/* we found a solaris partition to use */
char part_name[MAXPATHLEN];
if (conv_flag) {
/* convert part. name (e.g. c0d0p1) */
(void) snprintf(part_name, sizeof (part_name),
"%s%d", pname, i+1);
} else {
(void) snprintf(part_name, sizeof (part_name),
"%d", i+1);
}
/* the media name comes from the slice desc. */
partitions[0] = cache_get_desc(DM_PARTITION,
desc->p.disk, part_name, desc->secondary_name,
errp);
if (*errp != 0) {
cache_free_descriptors(partitions);
return (NULL);
}
partitions[1] = NULL;
return (partitions);
}
return (libdiskmgt_empty_desc_array(errp));
}
/* Must be for media, so get all the parts. */
pos = 0;
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].systid != UNUSED) {
char part_name[MAXPATHLEN];
/*
* Process the descriptors and modify the cxdxpx
* format so that it refers to the fdisk partition
* number and not to the physical disk. This is
* achieved by i+1, where i is the number of the
* physical disk partition.
*/
if (conv_flag) {
/* convert part. name (e.g. c0d0p1) */
(void) snprintf(part_name, sizeof (part_name),
"%s%d", pname, i+1);
} else {
(void) snprintf(part_name, sizeof (part_name),
"%d", i+1);
}
/* the media name comes from the media desc. */
partitions[pos] = cache_get_desc(DM_PARTITION,
desc->p.disk, part_name, desc->name, errp);
if (*errp != 0) {
cache_free_descriptors(partitions);
return (NULL);
}
pos++;
}
}
partitions[pos] = NULL;
*errp = 0;
return (partitions);
}
nvlist_t *
partition_get_attributes(descriptor_t *dp, int *errp)
{
nvlist_t *attrs = NULL;
struct ipart iparts[TOTAL_NUMPART];
if (!desc_ok(dp)) {
*errp = ENODEV;
return (NULL);
}
if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) {
return (NULL);
}
if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
*errp = ENOMEM;
return (NULL);
}
if ((*errp = get_attrs(dp, iparts, attrs)) != 0) {
nvlist_free(attrs);
attrs = NULL;
}
return (attrs);
}
/*
* Look for the partition by the partition number (which is not too useful).
*/
descriptor_t *
partition_get_descriptor_by_name(char *name, int *errp)
{
descriptor_t **partitions;
int i;
descriptor_t *partition = NULL;
partitions = cache_get_descriptors(DM_PARTITION, errp);
if (*errp != 0) {
return (NULL);
}
for (i = 0; partitions[i]; i++) {
if (libdiskmgt_str_eq(name, partitions[i]->name)) {
partition = partitions[i];
} else {
/* clean up the unused descriptors */
cache_free_descriptor(partitions[i]);
}
}
free(partitions);
if (partition == NULL) {
*errp = ENODEV;
}
return (partition);
}
/* ARGSUSED */
descriptor_t **
partition_get_descriptors(int filter[], int *errp)
{
return (cache_get_descriptors(DM_PARTITION, errp));
}
char *
partition_get_name(descriptor_t *desc)
{
return (desc->name);
}
/* ARGSUSED */
nvlist_t *
partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
{
/* There are no stat types defined for partitions */
*errp = EINVAL;
return (NULL);
}
/* ARGSUSED */
int
partition_has_fdisk(disk_t *dp, int fd)
{
char bootsect[512 * 3]; /* 3 sectors to be safe */
#ifdef sparc
if (dp->drv_type == DM_DT_FIXED) {
/* on sparc, only removable media can have fdisk parts. */
return (0);
}
#endif
/*
* We assume the caller already made sure media was inserted and
* spun up.
*/
if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
return (0);
}
return (1);
}
/*
* partition_make_descriptors
*
* A partition descriptor points to a disk, the name is the partition number
* and the secondary name is the media name. The iparts parameter returned
* by the get_parts function contains the structures of all of the identified
* partitions found on each disk on a system. These are processed into an array
* of descriptors. A descriptor contains all of the information about a
* specific partition.
*
* Parameters: none
*
* Returns: 0 on success
* Error value on failure
*
*/
int
partition_make_descriptors()
{
int error;
disk_t *dp;
dp = cache_get_disklist();
while (dp != NULL) {
struct ipart iparts[TOTAL_NUMPART];
char pname[MAXPATHLEN];
if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) {
int i;
char mname[MAXPATHLEN];
int conv_flag = 0;
#if defined(i386) || defined(__amd64)
/* convert part. name (e.g. c0d0p1) */
int len;
len = strlen(pname);
if (len > 1 && *(pname + (len - 2)) == 'p') {
conv_flag = 1;
*(pname + (len - 1)) = 0;
}
#endif
mname[0] = 0;
(void) media_read_name(dp, mname, sizeof (mname));
/*
* Process the descriptors and modify the cxdxpx
* format so that it refers to the fdisk partition
* number and not to the physical disk. This is
* achieved by i+1, where i is the number of the
* physical disk partition.
*/
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].systid != UNUSED) {
char part_name[MAXPATHLEN];
if (conv_flag) {
/*
* convert partition name
* (e.g. c0d0p1)
*/
(void) snprintf(part_name,
sizeof (part_name),
"%s%d", pname, i+1);
} else {
(void) snprintf(part_name,
sizeof (part_name),
"%d", i+1);
}
cache_load_desc(DM_PARTITION, dp,
part_name, mname, &error);
if (error != 0) {
return (error);
}
}
}
}
dp = dp->next;
}
return (0);
}
static int
get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs)
{
char *p;
int part_num;
/*
* We already made sure the media was loaded and ready in the
* get_parts call within partition_get_attributes.
*/
p = strrchr(dp->name, 'p');
if (p == NULL) {
p = dp->name;
} else {
p++;
}
part_num = atoi(p);
if (part_num > TOTAL_NUMPART ||
iparts[part_num - 1].systid == UNUSED) {
return (ENODEV);
}
/*
* A partition has been found. Determine what type of
* partition it is: logical, extended, or primary.
* Collect the information for the partition.
*/
#if defined(i386) || defined(__amd64)
if (part_num > FD_NUMPART) {
if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
DM_LOGICAL) != 0) {
return (ENOMEM);
}
} else if (fdisk_is_dos_extended(iparts[part_num - 1].systid)) {
if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
DM_EXTENDED) != 0) {
return (ENOMEM);
}
} else {
if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
DM_PRIMARY) != 0) {
return (ENOMEM);
}
}
#endif
#ifdef sparc
if (nvlist_add_uint32(attrs, DM_PARTITION_TYPE,
DM_PRIMARY) != 0) {
return (ENOMEM);
}
#endif
if (nvlist_add_uint32(attrs, DM_BOOTID,
(unsigned int)iparts[part_num - 1].bootid) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_PTYPE,
(unsigned int)iparts[part_num - 1].systid) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_BHEAD,
(unsigned int)iparts[part_num - 1].beghead) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_BSECT,
(unsigned int)((iparts[part_num - 1].begsect) & 0x3f)) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int)
((iparts[part_num - 1].begcyl & 0xff) |
((iparts[part_num - 1].begsect & 0xc0) << 2))) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_EHEAD,
(unsigned int)iparts[part_num - 1].endhead) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_ESECT,
(unsigned int)((iparts[part_num - 1].endsect) & 0x3f)) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int)
((iparts[part_num - 1].endcyl & 0xff) |
((iparts[part_num - 1].endsect & 0xc0) << 2))) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_RELSECT,
(unsigned int)iparts[part_num - 1].relsect) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_NSECTORS,
(unsigned int)iparts[part_num - 1].numsect) != 0) {
return (ENOMEM);
}
return (0);
}
/*
* get_parts
* Discovers the primary, extended, and logical partitions that have
* been created on a disk. get_parts loops through the partitions,
* collects the information on each partition and stores it in a
* partition table.
*
* Parameters;
* disk -The disk device to be evaluated for partitions
* iparts -The structure that holds information about
* the partitions
* opath -The device path
* opath_len -Buffer size used with opath
* Returns:
* 0 on Successful completion
* Error Value on failure
*
*/
static int
get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len)
{
int fd;
struct dk_minfo minfo;
struct mboot bootblk;
char bootsect[512];
int i;
#if defined(i386) || defined(__amd64)
int j, ret;
ext_part_t *epp; /* extended partition structure */
char *device; /* name of fixed disk drive */
size_t len;
logical_drive_t *log_drv; /* logical drive structure */
uint64_t tmpsect;
#endif
/* Can't use drive_open_disk since we need the partition dev name. */
if ((fd = open_disk(disk, opath, opath_len)) < 0) {
return (ENODEV);
}
/* First make sure media is inserted and spun up. */
if (!media_read_info(fd, &minfo)) {
(void) close(fd);
return (ENODEV);
}
if (!partition_has_fdisk(disk, fd)) {
(void) close(fd);
return (ENOTTY);
}
if (lseek(fd, 0, 0) == -1) {
(void) close(fd);
return (ENODEV);
}
if (read(fd, bootsect, 512) != 512) {
(void) close(fd);
return (ENODEV);
}
(void) close(fd);
(void) memcpy(&bootblk, bootsect, sizeof (bootblk));
if (les(bootblk.signature) != MBB_MAGIC) {
return (ENOTTY);
}
/*
* Initialize the memory space to clear unknown garbage
* that might create confusing results.
*/
for (i = 0; i < TOTAL_NUMPART; i++) {
(void) memset(&iparts[i], 0, sizeof (struct ipart));
iparts[i].systid = UNUSED;
}
(void) memcpy(iparts, bootblk.parts, ISIZE);
/*
* Check to see if a valid partition exists. If a valid partition
* exists, check to see if it is an extended partition.
* If an extended partition exists, collect the logical partition
* data.
*/
for (i = 0; i < FD_NUMPART; i++) {
if (iparts[i].systid == UNUSED)
continue;
iparts[i].relsect = lel(iparts[i].relsect);
iparts[i].numsect = lel(iparts[i].numsect);
#if defined(i386) || defined(__amd64)
if (!fdisk_is_dos_extended(iparts[i].systid))
continue;
len = strlen(disk->aliases->alias) + 1;
if ((device = malloc(len)) == NULL) {
if (device)
free(device);
continue;
}
(void) snprintf(device, len, "%s", disk->aliases->alias);
if ((ret = libfdisk_init(&epp, device, &iparts[i],
FDISK_READ_DISK)) != FDISK_SUCCESS) {
switch (ret) {
/*
* The first 2 error cases indicate that
* there is no Solaris logical partition,
* which is a valid condition,
* so iterating through the disk continues.
* Any other error cases indicate there is
* a potential problem with the disk, so
* don't continue iterating through the disk
* and return an error.
*/
case FDISK_EBADLOGDRIVE:
case FDISK_ENOLOGDRIVE:
free(device);
libfdisk_fini(&epp);
continue;
case FDISK_EBADMAGIC:
free(device);
libfdisk_fini(&epp);
return (ENOTTY);
default:
free(device);
libfdisk_fini(&epp);
return (ENODEV);
}
}
/*
* Collect logical drive information
*/
for (log_drv = fdisk_get_ld_head(epp), j = FD_NUMPART,
tmpsect = 0; (j < TOTAL_NUMPART) && (log_drv != NULL);
log_drv = log_drv->next, j++) {
iparts[j].bootid = log_drv->parts[0].bootid;
iparts[j].beghead = log_drv->parts[0].beghead;
iparts[j].begsect = log_drv->parts[0].begsect;
iparts[j].begcyl = log_drv->parts[0].begcyl;
iparts[j].systid = log_drv->parts[0].systid;
iparts[j].endhead = log_drv->parts[0].endhead;
iparts[j].endsect = log_drv->parts[0].endsect;
iparts[j].endcyl = log_drv->parts[0].endcyl;
iparts[j].relsect = (tmpsect +
lel(log_drv->parts[0].relsect) + epp->ext_beg_sec);
iparts[j].numsect = lel(log_drv->parts[0].numsect);
tmpsect = lel(log_drv->parts[1].relsect);
}
/* free the device and the epp memory. */
free(device);
libfdisk_fini(&epp);
#endif
}
return (0);
}
/* return 1 if the partition descriptor is still valid, 0 if not. */
static int
desc_ok(descriptor_t *dp)
{
/* First verify the media name for removable media */
if (dp->p.disk->removable) {
char mname[MAXPATHLEN];
if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
return (0);
}
if (mname[0] == 0) {
return (libdiskmgt_str_eq(dp->secondary_name, NULL));
} else {
return (libdiskmgt_str_eq(dp->secondary_name, mname));
}
}
/*
* We could verify the partition is still there but this is kind of
* expensive and other code down the line will do that (e.g. see
* get_attrs).
*/
return (1);
}
/*
* Return 1 if partition has slices, 0 if not.
*/
static int
has_slices(descriptor_t *desc, int *errp)
{
int pnum;
int i;
char *p;
struct ipart iparts[TOTAL_NUMPART];
if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) {
*errp = ENODEV;
return (0);
}
p = strrchr(desc->name, 'p');
if (p == NULL) {
p = desc->name;
} else {
p++;
}
pnum = atoi(p);
/*
* Slices are associated with the active solaris partition or if there
* is no active solaris partition, then the first solaris partition.
*/
*errp = 0;
if (iparts[pnum].bootid == ACTIVE &&
(iparts[pnum].systid == SUNIXOS ||
iparts[pnum].systid == SUNIXOS2)) {
return (1);
} else {
int active = 0;
/* Check if there are no active solaris partitions. */
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].bootid == ACTIVE &&
(iparts[i].systid == SUNIXOS ||
iparts[i].systid == SUNIXOS2)) {
active = 1;
break;
}
}
if (!active) {
/* Check if this is the first solaris partition. */
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].systid == SUNIXOS ||
iparts[i].systid == SUNIXOS2) {
break;
}
}
if (i < TOTAL_NUMPART && i == pnum) {
return (1);
}
}
}
return (0);
}
static int
open_disk(disk_t *diskp, char *opath, int len)
{
/*
* Just open the first devpath.
*/
if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
#ifdef sparc
if (opath != NULL) {
(void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
}
return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
#else
/* On intel we need to open partition device (e.g. c0d0p1). */
char part_dev[MAXPATHLEN];
char *p;
(void) strlcpy(part_dev, diskp->aliases->devpaths->devpath,
sizeof (part_dev));
p = strrchr(part_dev, '/');
if (p == NULL) {
p = strrchr(part_dev, 's');
if (p != NULL) {
*p = 'p';
}
} else {
char *ps;
*p = 0;
ps = strrchr((p + 1), 's');
if (ps != NULL) {
*ps = 'p';
}
*p = '/';
}
if (opath != NULL) {
(void) strlcpy(opath, part_dev, len);
}
return (open(part_dev, O_RDONLY|O_NDELAY));
#endif
}
return (-1);
}