1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2017 Nexenta Systems, Inc.
29 */
30
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>
47
48 #include "libdiskmgt.h"
49 #include "disks_private.h"
50 #include "partition.h"
51 #ifndef VT_ENOTSUP
52 #define VT_ENOTSUP (-5)
53 #endif
54
55 #define FMT_UNKNOWN 0
56 #define FMT_VTOC 1
57 #define FMT_EFI 2
58
59 typedef int (*detectorp)(char *, nvlist_t *, int *);
60
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
70 };
71
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);
80
81 descriptor_t **
slice_get_assoc_descriptors(descriptor_t * desc,dm_desc_type_t type,int * errp)82 slice_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
83 int *errp)
84 {
85 if (!desc_ok(desc)) {
86 *errp = ENODEV;
87 return (NULL);
88 }
89
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));
95 }
96
97 *errp = EINVAL;
98 return (NULL);
99 }
100
101 /*
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.
106 */
107 descriptor_t **
slice_get_assocs(descriptor_t * desc,int * errp)108 slice_get_assocs(descriptor_t *desc, int *errp)
109 {
110 /* Just check the first drive name. */
111 if (desc->p.disk->aliases == NULL) {
112 *errp = 0;
113 return (libdiskmgt_empty_desc_array(errp));
114 }
115
116 return (get_fixed_assocs(desc, errp));
117 }
118
119 nvlist_t *
slice_get_attributes(descriptor_t * dp,int * errp)120 slice_get_attributes(descriptor_t *dp, int *errp)
121 {
122 nvlist_t *attrs = NULL;
123 int fd;
124 char devpath[MAXPATHLEN];
125
126 if (!desc_ok(dp)) {
127 *errp = ENODEV;
128 return (NULL);
129 }
130
131 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
132 *errp = ENOMEM;
133 return (NULL);
134 }
135
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);
139
140 if ((*errp = get_attrs(dp, fd, attrs)) != 0) {
141 nvlist_free(attrs);
142 attrs = NULL;
143 }
144
145 if (fd >= 0) {
146 (void) close(fd);
147 }
148
149 return (attrs);
150 }
151
152 /*
153 * Look for the slice by the slice devpath.
154 */
155 descriptor_t *
slice_get_descriptor_by_name(char * name,int * errp)156 slice_get_descriptor_by_name(char *name, int *errp)
157 {
158 int found = 0;
159 disk_t *dp;
160
161 for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) {
162 found = match_fixed_name(dp, name, errp);
163
164 if (found) {
165 char mname[MAXPATHLEN];
166
167 if (*errp != 0) {
168 return (NULL);
169 }
170
171 mname[0] = 0;
172 (void) media_read_name(dp, mname, sizeof (mname));
173
174 return (cache_get_desc(DM_SLICE, dp, name, mname,
175 errp));
176 }
177 }
178
179 *errp = ENODEV;
180 return (NULL);
181 }
182
183 /* ARGSUSED */
184 descriptor_t **
slice_get_descriptors(int filter[],int * errp)185 slice_get_descriptors(int filter[], int *errp)
186 {
187 return (cache_get_descriptors(DM_SLICE, errp));
188 }
189
190 char *
slice_get_name(descriptor_t * desc)191 slice_get_name(descriptor_t *desc)
192 {
193 return (desc->name);
194 }
195
196 nvlist_t *
slice_get_stats(descriptor_t * dp,int stat_type,int * errp)197 slice_get_stats(descriptor_t *dp, int stat_type, int *errp)
198 {
199 nvlist_t *stats;
200
201 if (stat_type != DM_SLICE_STAT_USE) {
202 *errp = EINVAL;
203 return (NULL);
204 }
205
206 *errp = 0;
207
208 if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) {
209 *errp = ENOMEM;
210 return (NULL);
211 }
212
213 if ((*errp = add_inuse(dp->name, stats)) != 0) {
214 nvlist_free(stats);
215 return (NULL);
216 }
217
218 return (stats);
219 }
220
221 /*
222 * A slice descriptor points to a disk, the name is the devpath and the
223 * secondary name is the media name.
224 */
225 int
slice_make_descriptors()226 slice_make_descriptors()
227 {
228 disk_t *dp;
229
230 dp = cache_get_disklist();
231 while (dp != NULL) {
232 int error;
233
234 error = make_fixed_descriptors(dp);
235 if (error != 0) {
236 return (error);
237 }
238
239 dp = dp->next;
240 }
241
242 return (0);
243 }
244
245 /* convert rdsk paths to dsk paths */
246 void
slice_rdsk2dsk(char * rdsk,char * dsk,int size)247 slice_rdsk2dsk(char *rdsk, char *dsk, int size)
248 {
249 char *strp;
250
251 (void) strlcpy(dsk, rdsk, size);
252
253 if ((strp = strstr(dsk, "/rdsk/")) == NULL) {
254 /* not rdsk, check for floppy */
255 strp = strstr(dsk, "/rdiskette");
256 }
257
258 if (strp != NULL) {
259 strp++; /* move ptr to the r in rdsk or rdiskette */
260
261 /* move the succeeding chars over by one */
262 do {
263 *strp = *(strp + 1);
264 strp++;
265 } while (*strp);
266 }
267 }
268
269 /*
270 * Check if/how the slice is used.
271 */
272 static int
add_inuse(char * name,nvlist_t * attrs)273 add_inuse(char *name, nvlist_t *attrs)
274 {
275 int i;
276 int error;
277
278 for (i = 0; detectors[i] != NULL; i ++) {
279 if (detectors[i](name, attrs, &error) || error != 0) {
280 if (error != 0) {
281 return (error);
282 }
283 break;
284 }
285 }
286
287 return (0);
288 }
289
290 /* return 1 if the slice descriptor is still valid, 0 if not. */
291 static int
desc_ok(descriptor_t * dp)292 desc_ok(descriptor_t *dp)
293 {
294 /* First verify the media name for removable media */
295 if (dp->p.disk->removable) {
296 char mname[MAXPATHLEN];
297
298 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
299 return (0);
300 }
301
302 if (mname[0] == 0) {
303 return (libdiskmgt_str_eq(dp->secondary_name, NULL));
304 } else {
305 return (libdiskmgt_str_eq(dp->secondary_name, mname));
306 }
307 }
308
309 /*
310 * We could verify the slice is still there, but other code down the
311 * line already does these checks (e.g. see get_attrs).
312 */
313
314 return (1);
315 }
316
317 /* convert dsk paths to rdsk paths */
318 static void
dsk2rdsk(char * dsk,char * rdsk,int size)319 dsk2rdsk(char *dsk, char *rdsk, int size)
320 {
321 char *slashp;
322 size_t len;
323
324 (void) strlcpy(rdsk, dsk, size);
325
326 /* make sure there is enough room to add the r to dsk */
327 len = strlen(dsk);
328 if (len + 2 > size) {
329 return;
330 }
331
332 if ((slashp = strstr(rdsk, "/dsk/")) == NULL) {
333 /* not dsk, check for floppy */
334 slashp = strstr(rdsk, "/diskette");
335 }
336
337 if (slashp != NULL) {
338 char *endp;
339
340 endp = rdsk + len; /* point to terminating 0 */
341 /* move the succeeding chars over by one */
342 do {
343 *(endp + 1) = *endp;
344 endp--;
345 } while (endp != slashp);
346
347 *(endp + 1) = 'r';
348 }
349 }
350
351 static int
get_attrs(descriptor_t * dp,int fd,nvlist_t * attrs)352 get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs)
353 {
354 struct dk_minfo minfo;
355 int status;
356 int data_format = FMT_UNKNOWN;
357 int snum = -1;
358 int error;
359 struct extvtoc vtoc;
360 struct dk_gpt *efip;
361 struct dk_cinfo dkinfo;
362 int cooked_fd;
363 struct stat buf;
364
365 if (fd < 0) {
366 return (ENODEV);
367 }
368
369 /* First make sure media is inserted and spun up. */
370 if (!media_read_info(fd, &minfo)) {
371 return (ENODEV);
372 }
373
374 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
375 data_format = FMT_VTOC;
376 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
377 data_format = FMT_EFI;
378 if (nvlist_add_boolean(attrs, DM_EFI) != 0) {
379 efi_free(efip);
380 return (ENOMEM);
381 }
382 }
383
384 if (data_format == FMT_UNKNOWN) {
385 return (ENODEV);
386 }
387
388 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
389 snum = dkinfo.dki_partition;
390 }
391
392 /* check the slice */
393 if (data_format == FMT_VTOC) {
394 if (snum < 0 || snum >= vtoc.v_nparts ||
395 vtoc.v_part[snum].p_size == 0) {
396 return (ENODEV);
397 }
398 } else { /* data_format == FMT_EFI */
399 if (snum < 0 || snum >= efip->efi_nparts ||
400 efip->efi_parts[snum].p_size == 0) {
401 efi_free(efip);
402 return (ENODEV);
403 }
404 }
405
406 /* the slice exists */
407
408 if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) {
409 if (data_format == FMT_EFI) {
410 efi_free(efip);
411 }
412 return (ENOMEM);
413 }
414
415 if (data_format == FMT_VTOC) {
416 if (nvlist_add_uint64(attrs, DM_START,
417 vtoc.v_part[snum].p_start) != 0) {
418 return (ENOMEM);
419 }
420
421 if (nvlist_add_uint64(attrs, DM_SIZE,
422 vtoc.v_part[snum].p_size) != 0) {
423 return (ENOMEM);
424 }
425
426 if (nvlist_add_uint32(attrs, DM_TAG,
427 vtoc.v_part[snum].p_tag) != 0) {
428 return (ENOMEM);
429 }
430
431 if (nvlist_add_uint32(attrs, DM_FLAG,
432 vtoc.v_part[snum].p_flag) != 0) {
433 return (ENOMEM);
434 }
435 } else { /* data_format == FMT_EFI */
436 if (nvlist_add_uint64(attrs, DM_START,
437 efip->efi_parts[snum].p_start) != 0) {
438 efi_free(efip);
439 return (ENOMEM);
440 }
441
442 if (nvlist_add_uint64(attrs, DM_SIZE,
443 efip->efi_parts[snum].p_size) != 0) {
444 efi_free(efip);
445 return (ENOMEM);
446 }
447
448 if (efip->efi_parts[snum].p_name[0] != 0) {
449 char label[EFI_PART_NAME_LEN + 1];
450
451 (void) snprintf(label, sizeof (label), "%.*s",
452 EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name);
453 if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) {
454 efi_free(efip);
455 return (ENOMEM);
456 }
457 }
458 }
459
460 if (data_format == FMT_EFI) {
461 efi_free(efip);
462 }
463
464 if (inuse_mnt(dp->name, attrs, &error)) {
465 if (error != 0)
466 return (error);
467 }
468
469 if (fstat(fd, &buf) != -1) {
470 if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) {
471 return (ENOMEM);
472 }
473 }
474
475 /*
476 * We need to open the cooked slice (not the raw one) to get the
477 * correct devid.
478 */
479 cooked_fd = open(dp->name, O_RDONLY|O_NDELAY);
480
481 if (cooked_fd >= 0) {
482 int no_mem = 0;
483 ddi_devid_t devid;
484
485 if (devid_get(cooked_fd, &devid) == 0) {
486 char *minor;
487
488 if (devid_get_minor_name(cooked_fd, &minor) == 0) {
489 char *devidstr;
490
491 devidstr = devid_str_encode(devid, minor);
492 if (devidstr != NULL) {
493
494 if (nvlist_add_string(attrs,
495 DM_DEVICEID, devidstr) != 0) {
496 no_mem = 1;
497 }
498
499 devid_str_free(devidstr);
500 }
501 devid_str_free(minor);
502 }
503 devid_free(devid);
504 }
505 (void) close(cooked_fd);
506
507 if (no_mem) {
508 return (ENOMEM);
509 }
510 }
511
512 return (0);
513 }
514
515 static descriptor_t **
get_fixed_assocs(descriptor_t * desc,int * errp)516 get_fixed_assocs(descriptor_t *desc, int *errp)
517 {
518 int fd;
519 int status;
520 int data_format = FMT_UNKNOWN;
521 int cnt;
522 struct extvtoc vtoc;
523 struct dk_gpt *efip;
524 int pos;
525 char *media_name = NULL;
526 slice_t *devp;
527 descriptor_t **slices;
528
529 if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) {
530 *errp = ENODEV;
531 return (NULL);
532 }
533
534 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
535 data_format = FMT_VTOC;
536 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
537 data_format = FMT_EFI;
538 } else {
539 (void) close(fd);
540 *errp = 0;
541 return (libdiskmgt_empty_desc_array(errp));
542 }
543 (void) close(fd);
544
545 /* count the number of slices */
546 devp = desc->p.disk->aliases->devpaths;
547 for (cnt = 0; devp != NULL; devp = devp->next)
548 cnt++;
549
550 /* allocate the array for the descriptors */
551 slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
552 if (slices == NULL) {
553 if (data_format == FMT_EFI) {
554 efi_free(efip);
555 }
556 *errp = ENOMEM;
557 return (NULL);
558 }
559
560 /* get the media name from the descriptor */
561 if (desc->type == DM_MEDIA) {
562 media_name = desc->name;
563 } else {
564 /* must be a DM_PARTITION */
565 media_name = desc->secondary_name;
566 }
567
568 pos = 0;
569 for (devp = desc->p.disk->aliases->devpaths; devp != NULL;
570 devp = devp->next) {
571
572 int slice_num;
573 char devpath[MAXPATHLEN];
574
575 slice_num = get_slice_num(devp);
576 /* can't get slicenum, so no need to keep trying the drive */
577 if (slice_num == -1) {
578 break;
579 }
580
581 if (data_format == FMT_VTOC) {
582 if (slice_num >= vtoc.v_nparts ||
583 vtoc.v_part[slice_num].p_size == 0) {
584 continue;
585 }
586 } else { /* data_format == FMT_EFI */
587 if (slice_num >= efip->efi_nparts ||
588 efip->efi_parts[slice_num].p_size == 0) {
589 continue;
590 }
591 }
592
593 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
594 slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath,
595 media_name, errp);
596 if (*errp != 0) {
597 cache_free_descriptors(slices);
598 if (data_format == FMT_EFI) {
599 efi_free(efip);
600 }
601 return (NULL);
602 }
603 pos++;
604 }
605 slices[pos] = NULL;
606
607 if (data_format == FMT_EFI) {
608 efi_free(efip);
609 }
610
611 *errp = 0;
612 return (slices);
613 }
614
615 static int
get_slice_num(slice_t * devp)616 get_slice_num(slice_t *devp)
617 {
618 /* check if we already determined the devpath slice number */
619 if (devp->slice_num == -1) {
620 int fd;
621
622 if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) {
623 struct dk_cinfo dkinfo;
624 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
625 devp->slice_num = dkinfo.dki_partition;
626 }
627 (void) close(fd);
628 }
629 }
630
631 return (devp->slice_num);
632 }
633
634 static int
make_fixed_descriptors(disk_t * dp)635 make_fixed_descriptors(disk_t *dp)
636 {
637 int error = 0;
638 alias_t *ap;
639 slice_t *devp;
640 char mname[MAXPATHLEN];
641 int data_format = FMT_UNKNOWN;
642 struct extvtoc vtoc;
643 struct dk_gpt *efip;
644
645 /* Just check the first drive name. */
646 if ((ap = dp->aliases) == NULL) {
647 return (0);
648 }
649
650 mname[0] = 0;
651 (void) media_read_name(dp, mname, sizeof (mname));
652
653 for (devp = ap->devpaths; devp != NULL; devp = devp->next) {
654 int slice_num;
655 char devpath[MAXPATHLEN];
656
657 slice_num = get_slice_num(devp);
658 /* can't get slicenum, so no need to keep trying the drive */
659 if (slice_num == -1) {
660 break;
661 }
662
663 if (data_format == FMT_UNKNOWN) {
664 int fd;
665 int status;
666
667 if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) {
668 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
669 data_format = FMT_VTOC;
670 } else if (status == VT_ENOTSUP &&
671 efi_alloc_and_read(fd, &efip) >= 0) {
672 data_format = FMT_EFI;
673 }
674 (void) close(fd);
675 }
676 }
677
678 /* can't get slice data, so no need to keep trying the drive */
679 if (data_format == FMT_UNKNOWN) {
680 break;
681 }
682
683 if (data_format == FMT_VTOC) {
684 if (slice_num >= vtoc.v_nparts ||
685 vtoc.v_part[slice_num].p_size == 0) {
686 continue;
687 }
688 } else { /* data_format == FMT_EFI */
689 if (slice_num >= efip->efi_nparts ||
690 efip->efi_parts[slice_num].p_size == 0) {
691 continue;
692 }
693 }
694
695 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
696 cache_load_desc(DM_SLICE, dp, devpath, mname, &error);
697 if (error != 0) {
698 break;
699 }
700 }
701
702 if (data_format == FMT_EFI) {
703 efi_free(efip);
704 }
705
706 return (error);
707 }
708
709 /*
710 * Just look for the name on the devpaths we have cached. Return 1 if we
711 * find the name and the size of that slice is non-zero.
712 */
713 static int
match_fixed_name(disk_t * diskp,char * name,int * errp)714 match_fixed_name(disk_t *diskp, char *name, int *errp)
715 {
716 slice_t *dp = NULL;
717 alias_t *ap;
718 int slice_num;
719 int fd;
720 int status;
721 int data_format = FMT_UNKNOWN;
722 struct extvtoc vtoc;
723 struct dk_gpt *efip;
724
725 ap = diskp->aliases;
726 while (ap != NULL) {
727 slice_t *devp;
728
729 devp = ap->devpaths;
730 while (devp != NULL) {
731 char path[MAXPATHLEN];
732
733 slice_rdsk2dsk(devp->devpath, path, sizeof (path));
734 if (libdiskmgt_str_eq(path, name)) {
735 /* found it */
736 dp = devp;
737 break;
738 }
739
740 devp = devp->next;
741 }
742
743 if (dp != NULL) {
744 break;
745 }
746
747 ap = ap->next;
748 }
749
750 if (dp == NULL) {
751 *errp = 0;
752 return (0);
753 }
754
755 /*
756 * If we found a match on the name we now have to check that this
757 * slice really exists (non-0 size).
758 */
759
760 slice_num = get_slice_num(dp);
761 /* can't get slicenum, so no slice */
762 if (slice_num == -1) {
763 *errp = ENODEV;
764 return (1);
765 }
766
767 if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) {
768 *errp = ENODEV;
769 return (1);
770 }
771
772 if ((status = read_extvtoc(fd, &vtoc)) >= 0) {
773 data_format = FMT_VTOC;
774 } else if (status == VT_ENOTSUP) {
775 status = efi_alloc_and_read(fd, &efip);
776 if (status >= 0) {
777 data_format = FMT_EFI;
778 } else if (status == VT_ERROR && errno == ENOTTY) {
779 *errp = 0;
780 return (1);
781 }
782 } else {
783 (void) close(fd);
784 *errp = ENODEV;
785 return (1);
786 }
787 (void) close(fd);
788
789 if (data_format == FMT_VTOC) {
790 if (slice_num < vtoc.v_nparts &&
791 vtoc.v_part[slice_num].p_size > 0) {
792 *errp = 0;
793 return (1);
794 }
795 } else { /* data_format == FMT_EFI */
796 if (slice_num < efip->efi_nparts &&
797 efip->efi_parts[slice_num].p_size > 0) {
798 efi_free(efip);
799 *errp = 0;
800 return (1);
801 }
802 efi_free(efip);
803 }
804
805 *errp = ENODEV;
806 return (1);
807 }
808