diff options
Diffstat (limited to 'usr/src/lib/libdiskmgt/common/partition.c')
| -rw-r--r-- | usr/src/lib/libdiskmgt/common/partition.c | 751 | 
1 files changed, 751 insertions, 0 deletions
| diff --git a/usr/src/lib/libdiskmgt/common/partition.c b/usr/src/lib/libdiskmgt/common/partition.c new file mode 100644 index 0000000000..92e3e9579a --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/partition.c @@ -0,0 +1,751 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 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 (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); +	} + +#ifdef i386 +	{ +	    /* 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 + +	/* +	 * 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; + +#ifdef i386 +		/* 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)) { +#ifdef i386 +	    /* XXX Work around bug 4725434 */ +	    if (disk->removable) { +#endif +	    (void) close(fd); +	    return (ENODEV); +#ifdef i386 +	    } +#endif +	} + +	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) +{ +	char	rmmedia_devpath[MAXPATHLEN]; + +	if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath, +	    sizeof (rmmedia_devpath))) { + +	    int		fd; +	    struct stat	buf; + +	    if (rmmedia_devpath[0] == 0) { +		/* removable but no media */ +		return (-1); +	    } + +	    if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) { +		return (-1); +	    } + +	    if (fstat(fd, &buf) != 0) { +		(void) close(fd); +		return (-1); +	    } + +	    if (buf.st_mode & S_IFCHR) { +		/* opened, is device, so done */ +		if (opath != NULL) { +		    (void) strlcpy(opath, rmmedia_devpath, len); +		} +		return (fd); + +	    } else if (buf.st_mode & S_IFDIR) { +		/* disk w/ slices so handle the directory */ +		DIR		*dirp; +		struct dirent	*dentp; +		int		dfd; +#ifdef _LP64 +		struct dirent	*result; +#endif + +		/* each device file in the dir represents a slice */ + +		if ((dirp = fdopendir(fd)) == NULL) { +		    (void) close(fd); +		    return (-1); +		} + +		if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) + +		    _PC_NAME_MAX + 1)) == NULL) { +		    /* out of memory */ +		    (void) close(fd); +		    return (-1); +		} +#ifdef _LP64 +		while (readdir_r(dirp, dentp, &result) != NULL) { +#else +		while (readdir_r(dirp, dentp) != NULL) { +#endif +		    char	slice_path[MAXPATHLEN]; + +		    if (libdiskmgt_str_eq(".", dentp->d_name) || +			libdiskmgt_str_eq("..", dentp->d_name)) { +			continue; +		    } + +		    (void) snprintf(slice_path, sizeof (slice_path), "%s/%s", +			rmmedia_devpath, dentp->d_name); + +		    if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) { +			continue; +		    } + +		    if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) { +			/* opened, is device, so done */ +			free(dentp); +			(void) close(fd); +			if (opath != NULL) { +			    (void) strlcpy(opath, slice_path, len); +			} +			return (dfd); +		    } + +		    /* not a device, keep looking */ +		    (void) close(dfd); +		} + +		/* did not find a device under the rmmedia_path */ +		free(dentp); +		(void) close(fd); +	    } + +	    /* didn't find a device under volume management control */ +	    return (-1); +	} + +	/* +	 * Not removable media under volume management control so 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); +} | 
