partition.c revision 18c2aff776a775d34a4c9893a4c72e0434d68e36
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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>
#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 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[FD_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(FD_NUMPART + 1,
sizeof (descriptor_t *));
if (partitions == NULL) {
*errp = ENOMEM;
return (NULL);
}
#if defined(i386) || defined(__amd64)
/* convert part. name (e.g. c0d0p0) */
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 < FD_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 >= FD_NUMPART) {
for (i = 0; i < FD_NUMPART; i++) {
if (iparts[i].systid == SUNIXOS ||
iparts[i].systid == SUNIXOS2) {
break;
}
}
}
if (i < FD_NUMPART) {
/* we found a solaris partition to use */
char part_name[MAXPATHLEN];
if (conv_flag) {
/* convert part. name (e.g. c0d0p0) */
(void) snprintf(part_name, sizeof (part_name), "%s%d",
pname, i);
} else {
(void) snprintf(part_name, sizeof (part_name), "%d", i);
}
/* 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 < FD_NUMPART; i++) {
if (iparts[i].systid != 0) {
char part_name[MAXPATHLEN];
if (conv_flag) {
/* convert part. name (e.g. c0d0p0) */
(void) snprintf(part_name, sizeof (part_name), "%s%d",
pname, i);
} else {
(void) snprintf(part_name, sizeof (part_name), "%d", i);
}
/* 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[FD_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);
}
/*
* A partition descriptor points to a disk, the name is the partition number
* and the secondary name is the media name.
*/
int
partition_make_descriptors()
{
int error;
disk_t *dp;
dp = cache_get_disklist();
while (dp != NULL) {
struct ipart iparts[FD_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. c0d0p0) */
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));
for (i = 0; i < FD_NUMPART; i++) {
if (iparts[i].systid != 0) {
char part_name[MAXPATHLEN];
if (conv_flag) {
/* convert part. name (e.g. c0d0p0) */
(void) snprintf(part_name, sizeof (part_name),
"%s%d", pname, i);
} else {
(void) snprintf(part_name, sizeof (part_name),
"%d", i);
}
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 >= FD_NUMPART || iparts[part_num].systid == 0) {
return (ENODEV);
}
/* we found the partition */
if (nvlist_add_uint32(attrs, DM_BOOTID,
(unsigned int)iparts[part_num].bootid) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_PTYPE,
(unsigned int)iparts[part_num].systid) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_BHEAD,
(unsigned int)iparts[part_num].beghead) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_BSECT,
(unsigned int)((iparts[part_num].begsect) & 0x3f)) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int)
((iparts[part_num].begcyl & 0xff) |
((iparts[part_num].begsect & 0xc0) << 2))) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_EHEAD,
(unsigned int)iparts[part_num].endhead) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_ESECT,
(unsigned int)((iparts[part_num].endsect) & 0x3f)) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int)
((iparts[part_num].endcyl & 0xff) |
((iparts[part_num].endsect & 0xc0) << 2))) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_RELSECT,
(unsigned int)iparts[part_num].relsect) != 0) {
return (ENOMEM);
}
if (nvlist_add_uint32(attrs, DM_NSECTORS,
(unsigned int)iparts[part_num].numsect) != 0) {
return (ENOMEM);
}
return (0);
}
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;
/* 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);
}
(void) memcpy(iparts, bootblk.parts, ISIZE);
for (i = 0; i < FD_NUMPART; i++) {
if (iparts[i].systid != 0) {
iparts[i].relsect = lel(iparts[i].relsect);
iparts[i].numsect = lel(iparts[i].numsect);
}
}
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[FD_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 < FD_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 < FD_NUMPART; i++) {
if (iparts[i].systid == SUNIXOS ||
iparts[i].systid == SUNIXOS2) {
break;
}
}
if (i < FD_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. c0d0p0). */
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);
}