diff options
Diffstat (limited to 'usr/src/lib/libdiskmgt/common/slice.c')
-rw-r--r-- | usr/src/lib/libdiskmgt/common/slice.c | 1399 |
1 files changed, 1399 insertions, 0 deletions
diff --git a/usr/src/lib/libdiskmgt/common/slice.c b/usr/src/lib/libdiskmgt/common/slice.c new file mode 100644 index 0000000000..607efeb120 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/slice.c @@ -0,0 +1,1399 @@ +/* + * 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 <dirent.h> +#include <sys/dkio.h> +#include <sys/stat.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <sys/vtoc.h> +#include <unistd.h> +#include <devid.h> +#include <dirent.h> +#include <sys/dktp/fdisk.h> +#include <sys/efi_partition.h> + +#include "libdiskmgt.h" +#include "disks_private.h" +#include "partition.h" +#ifndef VT_ENOTSUP +#define VT_ENOTSUP (-5) +#endif + +#define FMT_UNKNOWN 0 +#define FMT_VTOC 1 +#define FMT_EFI 2 + +static struct inuse_detectors { + int (*detectorp)(char *slice, nvlist_t *attrs, int *errp); + char *used_by; +} detectors[] = { + {inuse_mnt, DM_USE_MOUNT}, + {inuse_svm, DM_USE_SVM}, + {inuse_lu, DM_USE_LU}, + {inuse_dump, DM_USE_DUMP}, + {inuse_vxvm, DM_USE_VXVM}, + {inuse_fs, DM_USE_FS}, /* fs should always be last */ + {NULL, NULL} +}; + +static int add_inuse(char *name, nvlist_t *attrs); +static int desc_ok(descriptor_t *dp); +static void dsk2rdsk(char *dsk, char *rdsk, int size); +static int get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs); +static descriptor_t **get_fixed_assocs(descriptor_t *desc, int *errp); +static descriptor_t **get_removable_assocs(descriptor_t *desc, char *volm_path, + int *errp); +static int get_slice_num(slice_t *devp); +static int match_fixed_name(disk_t *dp, char *name, int *errp); +static int match_removable_name(disk_t *dp, char *name, int *errp); +static int make_fixed_descriptors(disk_t *dp); +static int make_removable_descriptors(disk_t *dp); +static int make_volm_dir_descriptors(disk_t *dp, int fd, + char *volm_path); +static int num_removable_slices(int fd, struct stat *bufp, + char *volm_path); + +descriptor_t ** +slice_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_PARTITION: + return (partition_get_assocs(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +/* + * This is called by media/partition to get the slice descriptors for the given + * media/partition descriptor. + * For media, just get the slices, but for a partition, it must be a solaris + * partition and if there are active partitions, it must be the active one. + */ +descriptor_t ** +slice_get_assocs(descriptor_t *desc, int *errp) +{ + int under_volm = 0; + char volm_path[MAXPATHLEN]; + + /* Just check the first drive name. */ + if (desc->p.disk->aliases == NULL) { + *errp = 0; + return (libdiskmgt_empty_desc_array(errp)); + } + + if (desc->p.disk->removable) { + if ((under_volm = media_get_volm_path(desc->p.disk, volm_path, + sizeof (volm_path)))) { + if (volm_path[0] == 0) { + /* no media */ + *errp = 0; + return (libdiskmgt_empty_desc_array(errp)); + } + } + } + + if (desc->p.disk->removable) { + if (under_volm) { + return (get_removable_assocs(desc, volm_path, errp)); + } else { + return (get_fixed_assocs(desc, errp)); + } + } else { + return (get_fixed_assocs(desc, errp)); + } +} + +nvlist_t * +slice_get_attributes(descriptor_t *dp, int *errp) +{ + nvlist_t *attrs = NULL; + int fd; + char devpath[MAXPATHLEN]; + + if (!desc_ok(dp)) { + *errp = ENODEV; + return (NULL); + } + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */ + dsk2rdsk(dp->name, devpath, sizeof (devpath)); + fd = open(devpath, O_RDONLY|O_NDELAY); + + if ((*errp = get_attrs(dp, fd, attrs)) != 0) { + nvlist_free(attrs); + attrs = NULL; + } + + if (fd >= 0) { + (void) close(fd); + } + + return (attrs); +} + +/* + * Look for the slice by the slice devpath. + */ +descriptor_t * +slice_get_descriptor_by_name(char *name, int *errp) +{ + int found = 0; + disk_t *dp; + + for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) { + if (dp->removable) { + found = match_removable_name(dp, name, errp); + } else { + found = match_fixed_name(dp, name, errp); + } + + if (found) { + char mname[MAXPATHLEN]; + + if (*errp != 0) { + return (NULL); + } + + mname[0] = 0; + (void) media_read_name(dp, mname, sizeof (mname)); + + return (cache_get_desc(DM_SLICE, dp, name, mname, errp)); + } + } + + *errp = ENODEV; + return (NULL); +} + +/* ARGSUSED */ +descriptor_t ** +slice_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_SLICE, errp)); +} + +char * +slice_get_name(descriptor_t *desc) +{ + return (desc->name); +} + +nvlist_t * +slice_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + nvlist_t *stats; + char *str; + + if (stat_type != DM_SLICE_STAT_USE) { + *errp = EINVAL; + return (NULL); + } + + *errp = 0; + + if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if ((*errp = add_inuse(dp->name, stats)) != 0) { + return (NULL); + } + + /* if no cluster use, check for a use of the local name */ + if (nvlist_lookup_string(stats, DM_USED_BY, &str) != 0) { + disk_t *diskp; + + diskp = dp->p.disk; + if (diskp->aliases != NULL && diskp->aliases->cluster) { + slice_t *sp; + int snum = -1; + struct dk_minfo minfo; + struct dk_cinfo dkinfo; + char devpath[MAXPATHLEN]; + int fd; + + /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */ + dsk2rdsk(dp->name, devpath, sizeof (devpath)); + fd = open(devpath, O_RDONLY|O_NDELAY); + + if (fd >= 0 && media_read_info(fd, &minfo) && + ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { + snum = dkinfo.dki_partition; + } + + if (fd >= 0) { + (void) close(fd); + } + + if (snum >= 0) { + for (sp = diskp->aliases->orig_paths; sp != NULL; + sp = sp->next) { + + if (sp->slice_num == snum) { + char localpath[MAXPATHLEN]; + + slice_rdsk2dsk(sp->devpath, localpath, + sizeof (localpath)); + + if ((*errp = add_inuse(localpath, stats)) != 0) { + return (NULL); + } + + break; + } + } + } + } + } + + return (stats); +} + +/* + * A slice descriptor points to a disk, the name is the devpath and the + * secondary name is the media name. + */ +int +slice_make_descriptors() +{ + disk_t *dp; + + dp = cache_get_disklist(); + while (dp != NULL) { + int error; + + if (dp->removable) { + error = make_removable_descriptors(dp); + } else { + error = make_fixed_descriptors(dp); + } + if (error != 0) { + return (error); + } + + dp = dp->next; + } + + return (0); +} + +/* convert rdsk paths to dsk paths */ +void +slice_rdsk2dsk(char *rdsk, char *dsk, int size) +{ + char *strp; + + (void) strlcpy(dsk, rdsk, size); + + if ((strp = strstr(dsk, "/rdsk/")) == NULL) { + /* not rdsk, check for floppy */ + strp = strstr(dsk, "/rdiskette"); + } + + if (strp != NULL) { + strp++; /* move ptr to the r in rdsk or rdiskette */ + + /* move the succeeding chars over by one */ + do { + *strp = *(strp + 1); + strp++; + } while (*strp); + } +} + +/* + * Check if/how the slice is used. + */ +static int +add_inuse(char *name, nvlist_t *attrs) +{ + int i; + int error; + + for (i = 0; detectors[i].detectorp != NULL; i ++) { + if ((detectors[i].detectorp)(name, attrs, &error) || error != 0) { + if (error != 0) { + return (error); + } + break; + } + } + + return (0); +} + +/* return 1 if the slice 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 slice is still there, but other code down the + * line already does these checks (e.g. see get_attrs). + */ + + return (1); +} + +/* convert dsk paths to rdsk paths */ +static void +dsk2rdsk(char *dsk, char *rdsk, int size) +{ + char *slashp; + size_t len; + + (void) strlcpy(rdsk, dsk, size); + + /* make sure there is enough room to add the r to dsk */ + len = strlen(dsk); + if (len + 2 > size) { + return; + } + + if ((slashp = strstr(rdsk, "/dsk/")) == NULL) { + /* not dsk, check for floppy */ + slashp = strstr(rdsk, "/diskette"); + } + + if (slashp != NULL) { + char *endp; + + endp = rdsk + len; /* point to terminating 0 */ + /* move the succeeding chars over by one */ + do { + *(endp + 1) = *endp; + endp--; + } while (endp != slashp); + + *(endp + 1) = 'r'; + } +} + +static int +get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs) +{ + struct dk_minfo minfo; + int status; + int data_format = FMT_UNKNOWN; + int snum = -1; + int error; + struct vtoc vtoc; + struct dk_gpt *efip; + struct dk_cinfo dkinfo; + disk_t *diskp; + char localpath[MAXPATHLEN]; + int cooked_fd; + struct stat buf; + int mntpnt = 0; + + if (fd < 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 (dp->p.disk->removable) +#endif + return (ENODEV); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + if (nvlist_add_boolean(attrs, DM_EFI) != 0) { + efi_free(efip); + return (ENOMEM); + } + } + + if (data_format == FMT_UNKNOWN) { + return (ENODEV); + } + + if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { + snum = dkinfo.dki_partition; + } + + /* check the slice */ + if (data_format == FMT_VTOC) { + if (snum < 0 || snum >= vtoc.v_nparts || + vtoc.v_part[snum].p_size == 0) { + return (ENODEV); + } + } else { /* data_format == FMT_EFI */ + if (snum < 0 || snum >= efip->efi_nparts || + efip->efi_parts[snum].p_size == 0) { + efi_free(efip); + return (ENODEV); + } + } + + /* the slice exists */ + + if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) { + if (data_format == FMT_EFI) { + efi_free(efip); + } + return (ENOMEM); + } + + if (data_format == FMT_VTOC) { + if (nvlist_add_uint64(attrs, DM_START, vtoc.v_part[snum].p_start) + != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size) + != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag) + != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag) + != 0) { + return (ENOMEM); + } + + } else { /* data_format == FMT_EFI */ + if (nvlist_add_uint64(attrs, DM_START, + efip->efi_parts[snum].p_start) != 0) { + efi_free(efip); + return (ENOMEM); + } + + if (nvlist_add_uint64(attrs, DM_SIZE, efip->efi_parts[snum].p_size) + != 0) { + efi_free(efip); + return (ENOMEM); + } + + if (efip->efi_parts[snum].p_name[0] != 0) { + char label[EFI_PART_NAME_LEN + 1]; + + (void) snprintf(label, sizeof (label), "%.*s", + EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name); + if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) { + efi_free(efip); + return (ENOMEM); + } + } + } + + if (data_format == FMT_EFI) { + efi_free(efip); + } + + if (inuse_mnt(dp->name, attrs, &error)) { + if (error != 0) { + return (error); + } + mntpnt = 1; + } + + /* + * Some extra attrs for cluster slices. + * + * get localname and possible mnt point for localpath + */ + localpath[0] = 0; + diskp = dp->p.disk; + if (diskp->aliases != NULL && diskp->aliases->cluster) { + slice_t *sp; + + for (sp = diskp->aliases->orig_paths; sp != NULL; sp = sp->next) { + if (sp->slice_num == -1) { + /* determine the slice number for this path */ + int sfd; + struct dk_cinfo dkinfo; + + if ((sfd = open(sp->devpath, O_RDONLY|O_NDELAY)) >= 0) { + if (ioctl(sfd, DKIOCINFO, &dkinfo) >= 0) { + sp->slice_num = dkinfo.dki_partition; + } + (void) close(sfd); + } + } + + if (sp->slice_num == snum) { + slice_rdsk2dsk(sp->devpath, localpath, sizeof (localpath)); + + if (nvlist_add_string(attrs, DM_LOCALNAME, localpath) + != 0) { + return (ENOMEM); + } + + if (mntpnt == 0) { + if (inuse_mnt(localpath, attrs, &error)) { + if (error != 0) { + return (error); + } + } + } + + break; + } + } + } + + if (fstat(fd, &buf) != -1) { + if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) { + return (ENOMEM); + } + } + + /* + * We need to open the cooked slice (not the raw one) to get the + * correct devid. Also see if we need to read the localpath for the + * cluster disk, since the minor name is unavailable for the did pseudo + * device. + */ + if (localpath[0] != 0) { + cooked_fd = open(localpath, O_RDONLY|O_NDELAY); + } else { + cooked_fd = open(dp->name, O_RDONLY|O_NDELAY); + } + + if (cooked_fd >= 0) { + int no_mem = 0; + ddi_devid_t devid; + + if (devid_get(cooked_fd, &devid) == 0) { + char *minor; + + if (devid_get_minor_name(cooked_fd, &minor) == 0) { + char *devidstr; + + if ((devidstr = devid_str_encode(devid, minor)) != 0) { + + if (nvlist_add_string(attrs, DM_DEVICEID, devidstr) + != 0) { + no_mem = 1; + } + + devid_str_free(devidstr); + } + devid_str_free(minor); + } + devid_free(devid); + } + (void) close(cooked_fd); + + if (no_mem) { + return (ENOMEM); + } + } + + return (0); +} + +static descriptor_t ** +get_fixed_assocs(descriptor_t *desc, int *errp) +{ + int fd; + int status; + int data_format = FMT_UNKNOWN; + int cnt; + struct vtoc vtoc; + struct dk_gpt *efip; + int pos; + char *media_name = NULL; + slice_t *devp; + descriptor_t **slices; + + if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) { + *errp = ENODEV; + return (NULL); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } else { + (void) close(fd); + *errp = 0; + return (libdiskmgt_empty_desc_array(errp)); + } + (void) close(fd); + + /* count the number of slices */ + for (cnt = 0, devp = desc->p.disk->aliases->devpaths; devp != NULL; + devp = devp->next, cnt++); + + /* allocate the array for the descriptors */ + slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (slices == NULL) { + if (data_format == FMT_EFI) { + efi_free(efip); + } + *errp = ENOMEM; + return (NULL); + } + + /* get the media name from the descriptor */ + if (desc->type == DM_MEDIA) { + media_name = desc->name; + } else { + /* must be a DM_PARTITION */ + media_name = desc->secondary_name; + } + + pos = 0; + for (devp = desc->p.disk->aliases->devpaths; devp != NULL; + devp = devp->next) { + + int slice_num; + char devpath[MAXPATHLEN]; + + slice_num = get_slice_num(devp); + /* can't get slicenum, so no need to keep trying the drive */ + if (slice_num == -1) { + break; + } + + if (data_format == FMT_VTOC) { + if (slice_num >= vtoc.v_nparts || + vtoc.v_part[slice_num].p_size == 0) { + continue; + } + } else { /* data_format == FMT_EFI */ + if (slice_num >= efip->efi_nparts || + efip->efi_parts[slice_num].p_size == 0) { + continue; + } + } + + slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); + slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath, + media_name, errp); + if (*errp != 0) { + cache_free_descriptors(slices); + if (data_format == FMT_EFI) { + efi_free(efip); + } + return (NULL); + } + pos++; + } + slices[pos] = NULL; + + if (data_format == FMT_EFI) { + efi_free(efip); + } + + *errp = 0; + return (slices); +} + +/* + * Called for loaded removable media under volume management control. + */ +static descriptor_t ** +get_removable_assocs(descriptor_t *desc, char *volm_path, int *errp) +{ + int pos; + int fd; + int cnt; + struct stat buf; + descriptor_t **slices; + char *media_name = NULL; + char devpath[MAXPATHLEN]; + + /* get the media name from the descriptor */ + if (desc->type == DM_MEDIA) { + media_name = desc->name; + } else { + /* must be a DM_PARTITION */ + media_name = desc->secondary_name; + } + + /* + * For removable media under volm control the volm_path will + * either be a device (if the media is made up of a single slice) or + * a directory (if the media has multiple slices) with the slices + * as devices contained in the directory. + */ + + if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) < 0 || + fstat(fd, &buf) != 0) { + *errp = ENODEV; + return (NULL); + } + + cnt = num_removable_slices(fd, &buf, volm_path); + + /* allocate the array for the descriptors */ + slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (slices == NULL) { + *errp = ENOMEM; + return (NULL); + } + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + pos = 0; + *errp = 0; + if (buf.st_mode & S_IFCHR) { + struct dk_minfo minfo; + + /* Make sure media has readable label */ + if (media_read_info(fd, &minfo)) { + int status; + int data_format = FMT_UNKNOWN; + struct vtoc vtoc; + struct dk_gpt *efip; + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && + efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } + + if (data_format != FMT_UNKNOWN) { + /* has a readable label */ + slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk, + devpath, media_name, errp); + } + } + + } else if (buf.st_mode & S_IFDIR) { + DIR *dirp; +#ifdef _LP64 + struct dirent *result; +#endif + + /* rewind, num_removable_slices already traversed */ + (void) lseek(fd, 0, SEEK_SET); + + if ((dirp = fdopendir(fd)) != NULL) { + struct dirent *dentp; + + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + int dfd; + int is_dev = 0; + 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", devpath, dentp->d_name); + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(dfd, &buf) == 0 && + buf.st_mode & S_IFCHR) { + is_dev = 1; + } + (void) close(dfd); + } + + if (!is_dev) { + continue; + } + + slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk, + slice_path, media_name, errp); + if (*errp != 0) { + break; + } + + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + } + } + + (void) close(fd); + + slices[pos] = NULL; + + if (*errp != 0) { + cache_free_descriptors(slices); + return (NULL); + } + + return (slices); +} + +static int +get_slice_num(slice_t *devp) +{ + /* check if we already determined the devpath slice number */ + if (devp->slice_num == -1) { + int fd; + + if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) { + struct dk_cinfo dkinfo; + if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { + devp->slice_num = dkinfo.dki_partition; + } + (void) close(fd); + } + } + + return (devp->slice_num); +} + +static int +make_fixed_descriptors(disk_t *dp) +{ + int error = 0; + alias_t *ap; + slice_t *devp; + char mname[MAXPATHLEN]; + int data_format = FMT_UNKNOWN; + struct vtoc vtoc; + struct dk_gpt *efip; + + /* Just check the first drive name. */ + if ((ap = dp->aliases) == NULL) { + return (0); + } + + mname[0] = 0; + (void) media_read_name(dp, mname, sizeof (mname)); + + for (devp = ap->devpaths; devp != NULL; devp = devp->next) { + int slice_num; + char devpath[MAXPATHLEN]; + + slice_num = get_slice_num(devp); + /* can't get slicenum, so no need to keep trying the drive */ + if (slice_num == -1) { + break; + } + + if (data_format == FMT_UNKNOWN) { + int fd; + int status; + + if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && + efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } + (void) close(fd); + } + } + + /* can't get slice data, so no need to keep trying the drive */ + if (data_format == FMT_UNKNOWN) { + break; + } + + if (data_format == FMT_VTOC) { + if (slice_num >= vtoc.v_nparts || + vtoc.v_part[slice_num].p_size == 0) { + continue; + } + } else { /* data_format == FMT_EFI */ + if (slice_num >= efip->efi_nparts || + efip->efi_parts[slice_num].p_size == 0) { + continue; + } + } + + slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); + cache_load_desc(DM_SLICE, dp, devpath, mname, &error); + if (error != 0) { + break; + } + } + + if (data_format == FMT_EFI) { + efi_free(efip); + } + + return (error); +} + +/* + * For removable media under volm control we have to do some special handling. + * We don't use the vtoc and /dev/dsk devpaths, since the slices are named + * under the /vol fs. + */ +static int +make_removable_descriptors(disk_t *dp) +{ + char volm_path[MAXPATHLEN]; + int error; + int fd; + + /* + * If this removable drive is not under volm control, just use + * normal handling. + */ + if (!media_get_volm_path(dp, volm_path, sizeof (volm_path))) { + return (make_fixed_descriptors(dp)); + } + + if (volm_path[0] == 0) { + /* no media */ + return (0); + } + + /* + * For removable media under volm control the rmmedia_devapth will + * either be a device (if the media is made up of a single slice) or + * a directory (if the media has multiple slices) with the slices + * as devices contained in the directory. + */ + error = 0; + if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(fd, &buf) == 0) { + if (buf.st_mode & S_IFCHR) { + int status; + int data_format = FMT_UNKNOWN; + struct dk_minfo minfo; + int error; + struct vtoc vtoc; + struct dk_gpt *efip; + char devpath[MAXPATHLEN]; + + /* Make sure media has readable label */ + if (!media_read_info(fd, &minfo)) { + /* no media */ + return (0); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && + efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } + + if (data_format == FMT_UNKNOWN) { + /* no readable label */ + return (0); + } + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + /* The media name is the volm_path in this case. */ + cache_load_desc(DM_SLICE, dp, devpath, volm_path, &error); + + } else if (buf.st_mode & S_IFDIR) { + /* each device file in the dir represents a slice */ + error = make_volm_dir_descriptors(dp, fd, volm_path); + } + } + (void) close(fd); + } + + return (error); +} + +/* + * This handles removable media with slices under volume management control. + * In this case we have a dir which is the media name and each slice on the + * media is a device file in this dir. + */ +static int +make_volm_dir_descriptors(disk_t *dp, int dirfd, char *volm_path) +{ + int error; + DIR *dirp; + struct dirent *dentp; +#ifdef _LP64 + struct dirent *result; +#endif + char devpath[MAXPATHLEN]; + + if ((dirp = fdopendir(dirfd)) == NULL) { + return (0); + } + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + error = 0; + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + int fd; + 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", + devpath, dentp->d_name); + + if ((fd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(fd, &buf) == 0 && buf.st_mode & S_IFCHR) { + /* The media name is the volm_path in this case. */ + cache_load_desc(DM_SLICE, dp, slice_path, volm_path, + &error); + if (error != 0) { + (void) close(fd); + break; + } + } + + (void) close(fd); + } + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + + return (error); +} + +/* + * Just look for the name on the devpaths we have cached. Return 1 if we + * find the name and the size of that slice is non-zero. + */ +static int +match_fixed_name(disk_t *diskp, char *name, int *errp) +{ + slice_t *dp = NULL; + alias_t *ap; + int slice_num; + int fd; + int status; + int data_format = FMT_UNKNOWN; + struct vtoc vtoc; + struct dk_gpt *efip; + + ap = diskp->aliases; + while (ap != NULL) { + slice_t *devp; + + devp = ap->devpaths; + while (devp != NULL) { + char path[MAXPATHLEN]; + + slice_rdsk2dsk(devp->devpath, path, sizeof (path)); + if (libdiskmgt_str_eq(path, name)) { + /* found it */ + dp = devp; + break; + } + + devp = devp->next; + } + + if (dp != NULL) { + break; + } + + ap = ap->next; + } + + if (dp == NULL) { + *errp = 0; + return (0); + } + + /* + * If we found a match on the name we now have to check that this + * slice really exists (non-0 size). + */ + + slice_num = get_slice_num(dp); + /* can't get slicenum, so no slice */ + if (slice_num == -1) { + *errp = ENODEV; + return (1); + } + + if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) { + *errp = ENODEV; + return (1); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } else { + (void) close(fd); + *errp = ENODEV; + return (1); + } + (void) close(fd); + + if (data_format == FMT_VTOC) { + if (slice_num < vtoc.v_nparts && + vtoc.v_part[slice_num].p_size > 0) { + *errp = 0; + return (1); + } + } else { /* data_format == FMT_EFI */ + if (slice_num < efip->efi_nparts && + efip->efi_parts[slice_num].p_size > 0) { + efi_free(efip); + *errp = 0; + return (1); + } + efi_free(efip); + } + + *errp = ENODEV; + return (1); +} + +static int +match_removable_name(disk_t *diskp, char *name, int *errp) +{ + char volm_path[MAXPATHLEN]; + int found; + int fd; + struct stat buf; + + /* + * If this removable drive is not under volm control, just use + * normal handling. + */ + if (!media_get_volm_path(diskp, volm_path, sizeof (volm_path))) { + return (match_fixed_name(diskp, name, errp)); + } + + if (volm_path[0] == 0) { + /* no media */ + *errp = 0; + return (0); + } + + /* + * For removable media under volm control the rmmedia_devapth will + * either be a device (if the media is made up of a single slice) or + * a directory (if the media has multiple slices) with the slices + * as devices contained in the directory. + */ + + *errp = 0; + + found = 0; + if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0 && + fstat(fd, &buf) == 0) { + + if (buf.st_mode & S_IFCHR) { + char devpath[MAXPATHLEN]; + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + if (libdiskmgt_str_eq(name, devpath)) { + found = 1; + } + + } else if (buf.st_mode & S_IFDIR) { + /* each device file in the dir represents a slice */ + DIR *dirp; + + if ((dirp = fdopendir(fd)) != NULL) { + struct dirent *dentp; +#ifdef _LP64 + struct dirent *result; +#endif + char devpath[MAXPATHLEN]; + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#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", devpath, dentp->d_name); + + if (libdiskmgt_str_eq(name, slice_path)) { + /* found name, check device */ + int dfd; + int is_dev = 0; + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) + >= 0) { + struct stat buf; + + if (fstat(dfd, &buf) == 0 && + buf.st_mode & S_IFCHR) { + is_dev = 1; + } + (void) close(dfd); + } + + /* we found the name */ + found = 1; + + if (!is_dev) { + *errp = ENODEV; + } + + break; + } + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + } + } /* end of dir handling */ + + (void) close(fd); + } + + return (found); +} + +static int +num_removable_slices(int fd, struct stat *bufp, char *volm_path) +{ + int cnt = 0; + + if (bufp->st_mode & S_IFCHR) { + cnt = 1; + + } else if (bufp->st_mode & S_IFDIR) { + /* each device file in the dir represents a slice */ + DIR *dirp; + + if ((dirp = fdopendir(fd)) != NULL) { + struct dirent *dentp; +#ifdef _LP64 + struct dirent *result; +#endif + char devpath[MAXPATHLEN]; + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + int dfd; + 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", devpath, dentp->d_name); + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(dfd, &buf) == 0 && + buf.st_mode & S_IFCHR) { + cnt++; + } + (void) close(dfd); + } + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + } + } /* end of dir handling */ + + return (cnt); +} |