#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);
}
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));
}
partitions = (descriptor_t **)calloc(TOTAL_NUMPART + 1,
sizeof (descriptor_t *));
if (partitions == NULL) {
*errp = ENOMEM;
return (NULL);
}
#if defined(i386) || defined(__amd64)
len = strlen(pname);
if (len > 1 && *(pname + (len - 2)) == 'p') {
conv_flag = 1;
*(pname + (len - 1)) = 0;
}
#endif
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;
}
}
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) {
char part_name[MAXPATHLEN];
if (conv_flag) {
(void) snprintf(part_name, sizeof (part_name),
"%s%d", pname, i+1);
} else {
(void) snprintf(part_name, sizeof (part_name),
"%d", i+1);
}
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));
}
pos = 0;
for (i = 0; i < TOTAL_NUMPART; i++) {
if (iparts[i].systid != UNUSED) {
char part_name[MAXPATHLEN];
if (conv_flag) {
(void) snprintf(part_name, sizeof (part_name),
"%s%d", pname, i+1);
} else {
(void) snprintf(part_name, sizeof (part_name),
"%d", i+1);
}
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);
}
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 {
cache_free_descriptor(partitions[i]);
}
}
free(partitions);
if (partition == NULL) {
*errp = ENODEV;
}
return (partition);
}
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);
}
nvlist_t *
partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
{
*errp = EINVAL;
return (NULL);
}
int
partition_has_fdisk(disk_t *dp, int fd)
{
char bootsect[512 * 3];
#ifdef sparc
if (dp->drv_type == DM_DT_FIXED) {
return (0);
}
#endif
if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
return (0);
}
return (1);
}
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)
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 < TOTAL_NUMPART; i++) {
if (iparts[i].systid != UNUSED) {
char part_name[MAXPATHLEN];
if (conv_flag) {
(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;
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);
}
#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);
}
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;
char *device;
size_t len;
logical_drive_t *log_drv;
uint64_t tmpsect;
#endif
if ((fd = open_disk(disk, opath, opath_len)) < 0) {
return (ENODEV);
}
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);
}
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);
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) {
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);
}
}
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(device);
libfdisk_fini(&epp);
#endif
}
return (0);
}
static int
desc_ok(descriptor_t *dp)
{
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));
}
}
return (1);
}
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);
*errp = 0;
if (iparts[pnum].bootid == ACTIVE &&
(iparts[pnum].systid == SUNIXOS ||
iparts[pnum].systid == SUNIXOS2)) {
return (1);
} else {
int active = 0;
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) {
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)
{
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
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);
}