/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2017 Nexenta Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 typedef int (*detectorp)(char *, nvlist_t *, int *); static detectorp detectors[] = { inuse_mnt, inuse_active_zpool, inuse_lu, inuse_dump, inuse_vxvm, inuse_exported_zpool, inuse_fs, /* fs should always be last */ 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 int get_slice_num(slice_t *devp); static int match_fixed_name(disk_t *dp, char *name, int *errp); static int make_fixed_descriptors(disk_t *dp); 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) { /* Just check the first drive name. */ if (desc->p.disk->aliases == NULL) { *errp = 0; return (libdiskmgt_empty_desc_array(errp)); } 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) { 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; 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) { nvlist_free(stats); return (NULL); } 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; 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] != NULL; i ++) { if (detectors[i](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 extvtoc vtoc; struct dk_gpt *efip; struct dk_cinfo dkinfo; int cooked_fd; struct stat buf; if (fd < 0) { return (ENODEV); } /* First make sure media is inserted and spun up. */ if (!media_read_info(fd, &minfo)) { return (ENODEV); } if ((status = read_extvtoc(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); } 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. */ 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; devidstr = devid_str_encode(devid, minor); if (devidstr != NULL) { 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 extvtoc 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_extvtoc(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 */ devp = desc->p.disk->aliases->devpaths; for (cnt = 0; 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); } 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 extvtoc 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_extvtoc(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); } /* * 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 extvtoc 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_extvtoc(fd, &vtoc)) >= 0) { data_format = FMT_VTOC; } else if (status == VT_ENOTSUP) { status = efi_alloc_and_read(fd, &efip); if (status >= 0) { data_format = FMT_EFI; } else if (status == VT_ERROR && errno == ENOTTY) { *errp = 0; return (1); } } 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); }