Merge branch 'merges' of git://repo.or.cz/unleashed into merges
[unleashed.git] / usr / src / lib / libdiskmgt / common / slice.c
blob2c2f9bbc44308c657ae4d7f993f934c333c2d863
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright 2017 Nexenta Systems, Inc.
31 #include <fcntl.h>
32 #include <libdevinfo.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <dirent.h>
37 #include <sys/dkio.h>
38 #include <sys/stat.h>
39 #include <sys/sunddi.h>
40 #include <sys/types.h>
41 #include <sys/vtoc.h>
42 #include <unistd.h>
43 #include <devid.h>
44 #include <dirent.h>
45 #include <sys/dktp/fdisk.h>
46 #include <sys/efi_partition.h>
48 #include "libdiskmgt.h"
49 #include "disks_private.h"
50 #include "partition.h"
51 #ifndef VT_ENOTSUP
52 #define VT_ENOTSUP (-5)
53 #endif
55 #define FMT_UNKNOWN 0
56 #define FMT_VTOC 1
57 #define FMT_EFI 2
59 typedef int (*detectorp)(char *, nvlist_t *, int *);
61 static detectorp detectors[] = {
62 inuse_mnt,
63 inuse_active_zpool,
64 inuse_lu,
65 inuse_dump,
66 inuse_vxvm,
67 inuse_exported_zpool,
68 inuse_fs, /* fs should always be last */
69 NULL
72 static int add_inuse(char *name, nvlist_t *attrs);
73 static int desc_ok(descriptor_t *dp);
74 static void dsk2rdsk(char *dsk, char *rdsk, int size);
75 static int get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs);
76 static descriptor_t **get_fixed_assocs(descriptor_t *desc, int *errp);
77 static int get_slice_num(slice_t *devp);
78 static int match_fixed_name(disk_t *dp, char *name, int *errp);
79 static int make_fixed_descriptors(disk_t *dp);
81 descriptor_t **
82 slice_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
83 int *errp)
85 if (!desc_ok(desc)) {
86 *errp = ENODEV;
87 return (NULL);
90 switch (type) {
91 case DM_MEDIA:
92 return (media_get_assocs(desc, errp));
93 case DM_PARTITION:
94 return (partition_get_assocs(desc, errp));
97 *errp = EINVAL;
98 return (NULL);
102 * This is called by media/partition to get the slice descriptors for the given
103 * media/partition descriptor.
104 * For media, just get the slices, but for a partition, it must be a solaris
105 * partition and if there are active partitions, it must be the active one.
107 descriptor_t **
108 slice_get_assocs(descriptor_t *desc, int *errp)
110 /* Just check the first drive name. */
111 if (desc->p.disk->aliases == NULL) {
112 *errp = 0;
113 return (libdiskmgt_empty_desc_array(errp));
116 return (get_fixed_assocs(desc, errp));
119 nvlist_t *
120 slice_get_attributes(descriptor_t *dp, int *errp)
122 nvlist_t *attrs = NULL;
123 int fd;
124 char devpath[MAXPATHLEN];
126 if (!desc_ok(dp)) {
127 *errp = ENODEV;
128 return (NULL);
131 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
132 *errp = ENOMEM;
133 return (NULL);
136 /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */
137 dsk2rdsk(dp->name, devpath, sizeof (devpath));
138 fd = open(devpath, O_RDONLY|O_NDELAY);
140 if ((*errp = get_attrs(dp, fd, attrs)) != 0) {
141 nvlist_free(attrs);
142 attrs = NULL;
145 if (fd >= 0) {
146 (void) close(fd);
149 return (attrs);
153 * Look for the slice by the slice devpath.
155 descriptor_t *
156 slice_get_descriptor_by_name(char *name, int *errp)
158 int found = 0;
159 disk_t *dp;
161 for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) {
162 found = match_fixed_name(dp, name, errp);
164 if (found) {
165 char mname[MAXPATHLEN];
167 if (*errp != 0) {
168 return (NULL);
171 mname[0] = 0;
172 (void) media_read_name(dp, mname, sizeof (mname));
174 return (cache_get_desc(DM_SLICE, dp, name, mname,
175 errp));
179 *errp = ENODEV;
180 return (NULL);
183 /* ARGSUSED */
184 descriptor_t **
185 slice_get_descriptors(int filter[], int *errp)
187 return (cache_get_descriptors(DM_SLICE, errp));
190 char *
191 slice_get_name(descriptor_t *desc)
193 return (desc->name);
196 nvlist_t *
197 slice_get_stats(descriptor_t *dp, int stat_type, int *errp)
199 nvlist_t *stats;
200 char *str;
202 if (stat_type != DM_SLICE_STAT_USE) {
203 *errp = EINVAL;
204 return (NULL);
207 *errp = 0;
209 if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) {
210 *errp = ENOMEM;
211 return (NULL);
214 if ((*errp = add_inuse(dp->name, stats)) != 0) {
215 nvlist_free(stats);
216 return (NULL);
219 /* if no cluster use, check for a use of the local name */
220 if (nvlist_lookup_string(stats, DM_USED_BY, &str) != 0) {
221 disk_t *diskp;
223 diskp = dp->p.disk;
224 if (diskp->aliases != NULL && diskp->aliases->cluster) {
225 slice_t *sp;
226 int snum = -1;
227 struct dk_minfo minfo;
228 struct dk_cinfo dkinfo;
229 char devpath[MAXPATHLEN];
230 int fd;
233 * dp->name is /dev/dsk, need to convert back to
234 * /dev/rdsk
236 dsk2rdsk(dp->name, devpath, sizeof (devpath));
237 fd = open(devpath, O_RDONLY|O_NDELAY);
239 if (fd >= 0 && media_read_info(fd, &minfo) &&
240 ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
241 snum = dkinfo.dki_partition;
244 if (fd >= 0) {
245 (void) close(fd);
248 if (snum >= 0) {
249 for (sp = diskp->aliases->orig_paths;
250 sp != NULL; sp = sp->next) {
252 if (sp->slice_num == snum) {
253 char localpath[MAXPATHLEN];
255 slice_rdsk2dsk(sp->devpath,
256 localpath,
257 sizeof (localpath));
259 *errp = add_inuse(localpath,
260 stats);
261 if (*errp != 0) {
262 nvlist_free(stats);
263 return (NULL);
266 break;
273 return (stats);
277 * A slice descriptor points to a disk, the name is the devpath and the
278 * secondary name is the media name.
281 slice_make_descriptors()
283 disk_t *dp;
285 dp = cache_get_disklist();
286 while (dp != NULL) {
287 int error;
289 error = make_fixed_descriptors(dp);
290 if (error != 0) {
291 return (error);
294 dp = dp->next;
297 return (0);
300 /* convert rdsk paths to dsk paths */
301 void
302 slice_rdsk2dsk(char *rdsk, char *dsk, int size)
304 char *strp;
306 (void) strlcpy(dsk, rdsk, size);
308 if ((strp = strstr(dsk, "/rdsk/")) == NULL) {
309 /* not rdsk, check for floppy */
310 strp = strstr(dsk, "/rdiskette");
313 if (strp != NULL) {
314 strp++; /* move ptr to the r in rdsk or rdiskette */
316 /* move the succeeding chars over by one */
317 do {
318 *strp = *(strp + 1);
319 strp++;
320 } while (*strp);
325 * Check if/how the slice is used.
327 static int
328 add_inuse(char *name, nvlist_t *attrs)
330 int i;
331 int error;
333 for (i = 0; detectors[i] != NULL; i ++) {
334 if (detectors[i](name, attrs, &error) || error != 0) {
335 if (error != 0) {
336 return (error);
338 break;
342 return (0);
345 /* return 1 if the slice descriptor is still valid, 0 if not. */
346 static int
347 desc_ok(descriptor_t *dp)
349 /* First verify the media name for removable media */
350 if (dp->p.disk->removable) {
351 char mname[MAXPATHLEN];
353 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
354 return (0);
357 if (mname[0] == 0) {
358 return (libdiskmgt_str_eq(dp->secondary_name, NULL));
359 } else {
360 return (libdiskmgt_str_eq(dp->secondary_name, mname));
365 * We could verify the slice is still there, but other code down the
366 * line already does these checks (e.g. see get_attrs).
369 return (1);
372 /* convert dsk paths to rdsk paths */
373 static void
374 dsk2rdsk(char *dsk, char *rdsk, int size)
376 char *slashp;
377 size_t len;
379 (void) strlcpy(rdsk, dsk, size);
381 /* make sure there is enough room to add the r to dsk */
382 len = strlen(dsk);
383 if (len + 2 > size) {
384 return;
387 if ((slashp = strstr(rdsk, "/dsk/")) == NULL) {
388 /* not dsk, check for floppy */
389 slashp = strstr(rdsk, "/diskette");
392 if (slashp != NULL) {
393 char *endp;
395 endp = rdsk + len; /* point to terminating 0 */
396 /* move the succeeding chars over by one */
397 do {
398 *(endp + 1) = *endp;
399 endp--;
400 } while (endp != slashp);
402 *(endp + 1) = 'r';
406 static int
407 get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs)
409 struct dk_minfo minfo;
410 int status;
411 int data_format = FMT_UNKNOWN;
412 int snum = -1;
413 int error;
414 struct extvtoc vtoc;
415 struct dk_gpt *efip;
416 struct dk_cinfo dkinfo;
417 disk_t *diskp;
418 char localpath[MAXPATHLEN];
419 int cooked_fd;
420 struct stat buf;
421 int mntpnt = 0;
423 if (fd < 0) {
424 return (ENODEV);
427 /* First make sure media is inserted and spun up. */
428 if (!media_read_info(fd, &minfo)) {
429 return (ENODEV);
432 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
433 data_format = FMT_VTOC;
434 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
435 data_format = FMT_EFI;
436 if (nvlist_add_boolean(attrs, DM_EFI) != 0) {
437 efi_free(efip);
438 return (ENOMEM);
442 if (data_format == FMT_UNKNOWN) {
443 return (ENODEV);
446 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
447 snum = dkinfo.dki_partition;
450 /* check the slice */
451 if (data_format == FMT_VTOC) {
452 if (snum < 0 || snum >= vtoc.v_nparts ||
453 vtoc.v_part[snum].p_size == 0) {
454 return (ENODEV);
456 } else { /* data_format == FMT_EFI */
457 if (snum < 0 || snum >= efip->efi_nparts ||
458 efip->efi_parts[snum].p_size == 0) {
459 efi_free(efip);
460 return (ENODEV);
464 /* the slice exists */
466 if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) {
467 if (data_format == FMT_EFI) {
468 efi_free(efip);
470 return (ENOMEM);
473 if (data_format == FMT_VTOC) {
474 if (nvlist_add_uint64(attrs, DM_START,
475 vtoc.v_part[snum].p_start) != 0) {
476 return (ENOMEM);
479 if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size)
480 != 0) {
481 return (ENOMEM);
484 if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag)
485 != 0) {
486 return (ENOMEM);
489 if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag)
490 != 0) {
491 return (ENOMEM);
494 } else { /* data_format == FMT_EFI */
495 if (nvlist_add_uint64(attrs, DM_START,
496 efip->efi_parts[snum].p_start) != 0) {
497 efi_free(efip);
498 return (ENOMEM);
501 if (nvlist_add_uint64(attrs, DM_SIZE,
502 efip->efi_parts[snum].p_size) != 0) {
503 efi_free(efip);
504 return (ENOMEM);
507 if (efip->efi_parts[snum].p_name[0] != 0) {
508 char label[EFI_PART_NAME_LEN + 1];
510 (void) snprintf(label, sizeof (label), "%.*s",
511 EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name);
512 if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) {
513 efi_free(efip);
514 return (ENOMEM);
519 if (data_format == FMT_EFI) {
520 efi_free(efip);
523 if (inuse_mnt(dp->name, attrs, &error)) {
524 if (error != 0) {
525 return (error);
527 mntpnt = 1;
531 * Some extra attrs for cluster slices.
533 * get localname and possible mnt point for localpath
535 localpath[0] = 0;
536 diskp = dp->p.disk;
537 if (diskp->aliases != NULL && diskp->aliases->cluster) {
538 slice_t *sp;
540 for (sp = diskp->aliases->orig_paths; sp != NULL;
541 sp = sp->next) {
542 if (sp->slice_num == -1) {
543 /* determine the slice number for this path */
544 int sfd;
545 struct dk_cinfo dkinfo;
547 sfd = open(sp->devpath, O_RDONLY|O_NDELAY);
548 if (sfd >= 0) {
549 if (ioctl(sfd, DKIOCINFO, &dkinfo)
550 >= 0) {
551 sp->slice_num =
552 dkinfo.dki_partition;
554 (void) close(sfd);
558 if (sp->slice_num == snum) {
559 slice_rdsk2dsk(sp->devpath, localpath,
560 sizeof (localpath));
562 if (nvlist_add_string(attrs, DM_LOCALNAME,
563 localpath) != 0) {
564 return (ENOMEM);
567 if (mntpnt == 0) {
568 if (inuse_mnt(localpath, attrs,
569 &error)) {
570 if (error != 0) {
571 return (error);
576 break;
581 if (fstat(fd, &buf) != -1) {
582 if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) {
583 return (ENOMEM);
588 * We need to open the cooked slice (not the raw one) to get the
589 * correct devid. Also see if we need to read the localpath for the
590 * cluster disk, since the minor name is unavailable for the did pseudo
591 * device.
593 if (localpath[0] != 0) {
594 cooked_fd = open(localpath, O_RDONLY|O_NDELAY);
595 } else {
596 cooked_fd = open(dp->name, O_RDONLY|O_NDELAY);
599 if (cooked_fd >= 0) {
600 int no_mem = 0;
601 ddi_devid_t devid;
603 if (devid_get(cooked_fd, &devid) == 0) {
604 char *minor;
606 if (devid_get_minor_name(cooked_fd, &minor) == 0) {
607 char *devidstr;
609 devidstr = devid_str_encode(devid, minor);
610 if (devidstr != NULL) {
612 if (nvlist_add_string(attrs,
613 DM_DEVICEID, devidstr) != 0) {
614 no_mem = 1;
617 devid_str_free(devidstr);
619 devid_str_free(minor);
621 devid_free(devid);
623 (void) close(cooked_fd);
625 if (no_mem) {
626 return (ENOMEM);
630 return (0);
633 static descriptor_t **
634 get_fixed_assocs(descriptor_t *desc, int *errp)
636 int fd;
637 int status;
638 int data_format = FMT_UNKNOWN;
639 int cnt;
640 struct extvtoc vtoc;
641 struct dk_gpt *efip;
642 int pos;
643 char *media_name = NULL;
644 slice_t *devp;
645 descriptor_t **slices;
647 if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) {
648 *errp = ENODEV;
649 return (NULL);
652 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
653 data_format = FMT_VTOC;
654 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
655 data_format = FMT_EFI;
656 } else {
657 (void) close(fd);
658 *errp = 0;
659 return (libdiskmgt_empty_desc_array(errp));
661 (void) close(fd);
663 /* count the number of slices */
664 devp = desc->p.disk->aliases->devpaths;
665 for (cnt = 0; devp != NULL; devp = devp->next)
666 cnt++;
668 /* allocate the array for the descriptors */
669 slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
670 if (slices == NULL) {
671 if (data_format == FMT_EFI) {
672 efi_free(efip);
674 *errp = ENOMEM;
675 return (NULL);
678 /* get the media name from the descriptor */
679 if (desc->type == DM_MEDIA) {
680 media_name = desc->name;
681 } else {
682 /* must be a DM_PARTITION */
683 media_name = desc->secondary_name;
686 pos = 0;
687 for (devp = desc->p.disk->aliases->devpaths; devp != NULL;
688 devp = devp->next) {
690 int slice_num;
691 char devpath[MAXPATHLEN];
693 slice_num = get_slice_num(devp);
694 /* can't get slicenum, so no need to keep trying the drive */
695 if (slice_num == -1) {
696 break;
699 if (data_format == FMT_VTOC) {
700 if (slice_num >= vtoc.v_nparts ||
701 vtoc.v_part[slice_num].p_size == 0) {
702 continue;
704 } else { /* data_format == FMT_EFI */
705 if (slice_num >= efip->efi_nparts ||
706 efip->efi_parts[slice_num].p_size == 0) {
707 continue;
711 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
712 slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath,
713 media_name, errp);
714 if (*errp != 0) {
715 cache_free_descriptors(slices);
716 if (data_format == FMT_EFI) {
717 efi_free(efip);
719 return (NULL);
721 pos++;
723 slices[pos] = NULL;
725 if (data_format == FMT_EFI) {
726 efi_free(efip);
729 *errp = 0;
730 return (slices);
733 static int
734 get_slice_num(slice_t *devp)
736 /* check if we already determined the devpath slice number */
737 if (devp->slice_num == -1) {
738 int fd;
740 if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) {
741 struct dk_cinfo dkinfo;
742 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
743 devp->slice_num = dkinfo.dki_partition;
745 (void) close(fd);
749 return (devp->slice_num);
752 static int
753 make_fixed_descriptors(disk_t *dp)
755 int error = 0;
756 alias_t *ap;
757 slice_t *devp;
758 char mname[MAXPATHLEN];
759 int data_format = FMT_UNKNOWN;
760 struct extvtoc vtoc;
761 struct dk_gpt *efip;
763 /* Just check the first drive name. */
764 if ((ap = dp->aliases) == NULL) {
765 return (0);
768 mname[0] = 0;
769 (void) media_read_name(dp, mname, sizeof (mname));
771 for (devp = ap->devpaths; devp != NULL; devp = devp->next) {
772 int slice_num;
773 char devpath[MAXPATHLEN];
775 slice_num = get_slice_num(devp);
776 /* can't get slicenum, so no need to keep trying the drive */
777 if (slice_num == -1) {
778 break;
781 if (data_format == FMT_UNKNOWN) {
782 int fd;
783 int status;
785 if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) {
786 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
787 data_format = FMT_VTOC;
788 } else if (status == VT_ENOTSUP &&
789 efi_alloc_and_read(fd, &efip) >= 0) {
790 data_format = FMT_EFI;
792 (void) close(fd);
796 /* can't get slice data, so no need to keep trying the drive */
797 if (data_format == FMT_UNKNOWN) {
798 break;
801 if (data_format == FMT_VTOC) {
802 if (slice_num >= vtoc.v_nparts ||
803 vtoc.v_part[slice_num].p_size == 0) {
804 continue;
806 } else { /* data_format == FMT_EFI */
807 if (slice_num >= efip->efi_nparts ||
808 efip->efi_parts[slice_num].p_size == 0) {
809 continue;
813 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
814 cache_load_desc(DM_SLICE, dp, devpath, mname, &error);
815 if (error != 0) {
816 break;
820 if (data_format == FMT_EFI) {
821 efi_free(efip);
824 return (error);
828 * Just look for the name on the devpaths we have cached. Return 1 if we
829 * find the name and the size of that slice is non-zero.
831 static int
832 match_fixed_name(disk_t *diskp, char *name, int *errp)
834 slice_t *dp = NULL;
835 alias_t *ap;
836 int slice_num;
837 int fd;
838 int status;
839 int data_format = FMT_UNKNOWN;
840 struct extvtoc vtoc;
841 struct dk_gpt *efip;
843 ap = diskp->aliases;
844 while (ap != NULL) {
845 slice_t *devp;
847 devp = ap->devpaths;
848 while (devp != NULL) {
849 char path[MAXPATHLEN];
851 slice_rdsk2dsk(devp->devpath, path, sizeof (path));
852 if (libdiskmgt_str_eq(path, name)) {
853 /* found it */
854 dp = devp;
855 break;
858 devp = devp->next;
861 if (dp != NULL) {
862 break;
865 ap = ap->next;
868 if (dp == NULL) {
869 *errp = 0;
870 return (0);
874 * If we found a match on the name we now have to check that this
875 * slice really exists (non-0 size).
878 slice_num = get_slice_num(dp);
879 /* can't get slicenum, so no slice */
880 if (slice_num == -1) {
881 *errp = ENODEV;
882 return (1);
885 if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) {
886 *errp = ENODEV;
887 return (1);
890 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
891 data_format = FMT_VTOC;
892 } else if (status == VT_ENOTSUP) {
893 status = efi_alloc_and_read(fd, &efip);
894 if (status >= 0) {
895 data_format = FMT_EFI;
896 } else if (status == VT_ERROR && errno == ENOTTY) {
897 *errp = 0;
898 return (1);
900 } else {
901 (void) close(fd);
902 *errp = ENODEV;
903 return (1);
905 (void) close(fd);
907 if (data_format == FMT_VTOC) {
908 if (slice_num < vtoc.v_nparts &&
909 vtoc.v_part[slice_num].p_size > 0) {
910 *errp = 0;
911 return (1);
913 } else { /* data_format == FMT_EFI */
914 if (slice_num < efip->efi_nparts &&
915 efip->efi_parts[slice_num].p_size > 0) {
916 efi_free(efip);
917 *errp = 0;
918 return (1);
920 efi_free(efip);
923 *errp = ENODEV;
924 return (1);