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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <fcntl.h> 30 #include <libdevinfo.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <stropts.h> 36 #include <sys/dkio.h> 37 #include <sys/sunddi.h> 38 #include <sys/types.h> 39 #include <unistd.h> 40 #include <sys/vtoc.h> 41 #include <volmgt.h> 42 #include <sys/efi_partition.h> 43 44 #include "libdiskmgt.h" 45 #include "disks_private.h" 46 #include "partition.h" 47 48 #define IOCTLRETRIES 2 49 #define IOCTLRETRYINTERVAL 1 50 51 static descriptor_t **apply_filter(descriptor_t **media, int filter[], 52 int *errp); 53 static int get_attrs(disk_t *dp, int fd, nvlist_t *attrs); 54 static int get_non_volm_name(disk_t *dp, char *mname, int size); 55 static int get_media_type(uint_t media_type); 56 static int desc_ok(descriptor_t *dp); 57 58 /* 59 * This function gets the descriptors we are associated with. 60 */ 61 descriptor_t ** 62 media_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, 63 int *errp) 64 { 65 if (!desc_ok(desc)) { 66 *errp = ENODEV; 67 return (NULL); 68 } 69 70 switch (type) { 71 case DM_DRIVE: 72 return (drive_get_assocs(desc, errp)); 73 case DM_PARTITION: 74 return (partition_get_assocs(desc, errp)); 75 case DM_SLICE: 76 return (slice_get_assocs(desc, errp)); 77 } 78 79 *errp = EINVAL; 80 return (NULL); 81 } 82 83 /* 84 * Get the media descriptors for the given drive/partition/slice. 85 */ 86 descriptor_t ** 87 media_get_assocs(descriptor_t *dp, int *errp) 88 { 89 descriptor_t **media; 90 char mname[MAXPATHLEN]; 91 92 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { 93 /* For drives, this means no media but slice/part. require media. */ 94 if (dp->type == DM_DRIVE) { 95 return (libdiskmgt_empty_desc_array(errp)); 96 } else { 97 *errp = ENODEV; 98 return (NULL); 99 } 100 } 101 102 /* make the snapshot */ 103 media = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); 104 if (media == NULL) { 105 *errp = ENOMEM; 106 return (NULL); 107 } 108 109 media[0] = cache_get_desc(DM_MEDIA, dp->p.disk, mname, NULL, errp); 110 if (*errp != 0) { 111 free(media); 112 return (NULL); 113 } 114 media[1] = NULL; 115 116 *errp = 0; 117 return (media); 118 } 119 120 nvlist_t * 121 media_get_attributes(descriptor_t *dp, int *errp) 122 { 123 nvlist_t *attrs = NULL; 124 int fd; 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 fd = drive_open_disk(dp->p.disk, NULL, 0); 137 138 if ((*errp = get_attrs(dp->p.disk, fd, attrs)) != 0) { 139 nvlist_free(attrs); 140 attrs = NULL; 141 } 142 143 if (fd >= 0) { 144 (void) close(fd); 145 } 146 147 return (attrs); 148 } 149 150 descriptor_t * 151 media_get_descriptor_by_name(char *name, int *errp) 152 { 153 descriptor_t **media; 154 int i; 155 descriptor_t *medium = NULL; 156 157 media = cache_get_descriptors(DM_MEDIA, errp); 158 if (*errp != 0) { 159 return (NULL); 160 } 161 162 for (i = 0; media[i]; i++) { 163 if (libdiskmgt_str_eq(name, media[i]->name)) { 164 medium = media[i]; 165 } else { 166 /* clean up the unused descriptors */ 167 cache_free_descriptor(media[i]); 168 } 169 } 170 free(media); 171 172 if (medium == NULL) { 173 *errp = ENODEV; 174 } 175 176 return (medium); 177 } 178 179 descriptor_t ** 180 media_get_descriptors(int filter[], int *errp) 181 { 182 descriptor_t **media; 183 184 media = cache_get_descriptors(DM_MEDIA, errp); 185 if (*errp != 0) { 186 return (NULL); 187 } 188 189 if (filter != NULL && filter[0] != DM_FILTER_END) { 190 descriptor_t **found; 191 192 found = apply_filter(media, filter, errp); 193 if (*errp != 0) { 194 media = NULL; 195 } else { 196 media = found; 197 } 198 } 199 200 return (media); 201 } 202 203 char * 204 media_get_name(descriptor_t *desc) 205 { 206 return (desc->name); 207 } 208 209 /* ARGSUSED */ 210 nvlist_t * 211 media_get_stats(descriptor_t *dp, int stat_type, int *errp) 212 { 213 /* There are no stat types defined for media */ 214 *errp = EINVAL; 215 return (NULL); 216 } 217 218 /* 219 * Get the removable media volume manager devpath for the disk. This is the 220 * name we need to open that will work with vold. 221 * Return 1 if under volm control, 0 if not under volm control. 222 * The string in mediapath will be empty if the drive is under volm control 223 * but there is no media loaded. 224 */ 225 int 226 media_get_volm_path(disk_t *diskp, char *mediapath, int size) 227 { 228 char vname[MAXPATHLEN]; 229 char *volname; 230 char *media_name; 231 232 if (!diskp->removable || !volmgt_running()) { 233 return (0); 234 } 235 236 /* 237 * The volume manager is running, so we have to check if this removable 238 * drive is under volm control or not. 239 */ 240 241 /* 242 * We must check if this drive is under volume management control and 243 * what devpath to use. 244 * Note that we have to do this every time for drives that are not 245 * under the control of the volume manager, since the volume manager 246 * might have taken control since the last time we checked. 247 */ 248 if (diskp->volm_path_set == 0) { 249 alias_t *ap; 250 slice_t *dp; 251 252 if ((ap = diskp->aliases) == NULL) { 253 return (0); 254 } 255 256 /* Check each devpath to see if it is under volm control. */ 257 dp = ap->devpaths; 258 while (dp != NULL) { 259 slice_rdsk2dsk(dp->devpath, vname, sizeof (vname)); 260 if (volmgt_inuse(vname)) { 261 break; 262 } 263 264 dp = dp->next; 265 } 266 267 if (dp != NULL) { 268 /* Volume manager is managing the devpath that dp points to. */ 269 diskp->volm_path = dp->devpath; 270 diskp->volm_path_set = 1; 271 } 272 } 273 274 if (diskp->volm_path_set == 0) { 275 /* The volume manager is not managing any of the devpaths. */ 276 return (0); 277 } 278 279 if (dm_debug > 1) { 280 (void) fprintf(stderr, "INFO: chk vol: %s\n", diskp->volm_path); 281 } 282 283 slice_rdsk2dsk(diskp->volm_path, vname, sizeof (vname)); 284 volname = volmgt_symname(vname); 285 if (volname == NULL) { 286 mediapath[0] = 0; 287 return (1); 288 } 289 290 media_name = media_findname(volname); 291 free(volname); 292 if (media_name == NULL) { 293 mediapath[0] = 0; 294 return (1); 295 } 296 297 (void) strlcpy(mediapath, media_name, size); 298 free(media_name); 299 return (1); 300 } 301 302 int 303 media_make_descriptors() 304 { 305 int error; 306 disk_t *dp; 307 char mname[MAXPATHLEN]; 308 309 dp = cache_get_disklist(); 310 while (dp != NULL) { 311 if (media_read_name(dp, mname, sizeof (mname))) { 312 cache_load_desc(DM_MEDIA, dp, mname, NULL, &error); 313 if (error != 0) { 314 return (error); 315 } 316 } 317 318 dp = dp->next; 319 } 320 321 return (0); 322 } 323 324 /* 325 * Read the media information. 326 */ 327 int 328 media_read_info(int fd, struct dk_minfo *minfo) 329 { 330 int status; 331 int tries = 0; 332 333 minfo->dki_media_type = 0; 334 335 /* 336 * This ioctl can fail if the media is not loaded or spun up. 337 * Retrying can sometimes succeed since the first ioctl will have 338 * started the media before the ioctl timed out so the media may be 339 * spun up on the subsequent attempt. 340 */ 341 while ((status = ioctl(fd, DKIOCGMEDIAINFO, minfo)) < 0) { 342 tries++; 343 if (tries >= IOCTLRETRIES) { 344 break; 345 } 346 (void) sleep(IOCTLRETRYINTERVAL); 347 } 348 349 if (status < 0) { 350 return (0); 351 } 352 353 return (1); 354 } 355 356 /* return 1 if there is media, 0 if not. */ 357 int 358 media_read_name(disk_t *dp, char *mname, int size) 359 { 360 int under_volm; 361 char rmmedia_devpath[MAXPATHLEN]; 362 363 mname[0] = 0; 364 365 if (!dp->removable) { 366 /* not removable, so media name is devid */ 367 if (dp->device_id != NULL) { 368 (void) strlcpy(mname, dp->device_id, size); 369 } 370 return (1); 371 } 372 373 /* This is a removable media drive. */ 374 375 /* Get 1 if under volm control, 0 if not */ 376 under_volm = media_get_volm_path(dp, rmmedia_devpath, 377 sizeof (rmmedia_devpath)); 378 379 if (under_volm) { 380 /* under volm control */ 381 if (rmmedia_devpath[0] == 0) { 382 /* no media */ 383 return (0); 384 } 385 (void) strlcpy(mname, rmmedia_devpath, size); 386 return (1); 387 388 } else { 389 /* not under volm control */ 390 return (get_non_volm_name(dp, mname, size)); 391 } 392 } 393 394 static descriptor_t ** 395 apply_filter(descriptor_t **media, int filter[], int *errp) 396 { 397 descriptor_t **found; 398 int i; 399 int cnt = 0; 400 int pos; 401 402 /* count the number of media in the snapshot */ 403 for (i = 0; media[i]; i++) { 404 cnt++; 405 } 406 407 found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 408 if (found == NULL) { 409 *errp = ENOMEM; 410 cache_free_descriptors(media); 411 return (NULL); 412 } 413 414 pos = 0; 415 for (i = 0; media[i]; i++) { 416 int fd; 417 struct dk_minfo minfo; 418 419 if ((fd = drive_open_disk(media[i]->p.disk, NULL, 0)) < 0) { 420 continue; 421 } 422 423 if (media_read_info(fd, &minfo)) { 424 int mtype; 425 int j; 426 int match; 427 428 mtype = get_media_type(minfo.dki_media_type); 429 430 match = 0; 431 for (j = 0; filter[j] != DM_FILTER_END; j++) { 432 if (mtype == filter[j]) { 433 found[pos++] = media[i]; 434 match = 1; 435 break; 436 } 437 } 438 439 if (!match) { 440 cache_free_descriptor(media[i]); 441 } 442 } 443 #ifdef i386 444 /* XXX Work around bug 4725434 */ 445 else if (!media[i]->p.disk->removable) { 446 int j; 447 int match; 448 449 match = 0; 450 for (j = 0; filter[j] != DM_FILTER_END; j++) { 451 if (DM_MT_FIXED == filter[j]) { 452 found[pos++] = media[i]; 453 match = 1; 454 break; 455 } 456 } 457 458 if (!match) { 459 cache_free_descriptor(media[i]); 460 } 461 } 462 #endif 463 464 (void) close(fd); 465 } 466 found[pos] = NULL; 467 free(media); 468 469 *errp = 0; 470 return (found); 471 } 472 473 /* return 1 if the media descriptor is still valid, 0 if not. */ 474 static int 475 desc_ok(descriptor_t *dp) 476 { 477 /* First verify the media name for removable media */ 478 if (dp->p.disk->removable) { 479 char mname[MAXPATHLEN]; 480 481 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { 482 return (0); 483 } 484 485 if (mname[0] == 0) { 486 return (libdiskmgt_str_eq(dp->name, NULL)); 487 } else { 488 return (libdiskmgt_str_eq(dp->name, mname)); 489 } 490 } 491 492 return (1); 493 } 494 495 static int 496 get_attrs(disk_t *dp, int fd, nvlist_t *attrs) 497 { 498 struct dk_minfo minfo; 499 struct dk_geom geometry; 500 501 if (fd < 0) { 502 return (ENODEV); 503 } 504 505 bzero(&minfo, sizeof (struct dk_minfo)); 506 507 /* The first thing to do is read the media */ 508 if (!media_read_info(fd, &minfo)) { 509 /* XXX Work around bug 4725434 */ 510 #ifdef i386 511 if (dp->removable) 512 #endif 513 return (ENODEV); 514 } 515 516 if (partition_has_fdisk(dp, fd)) { 517 if (nvlist_add_boolean(attrs, DM_FDISK) != 0) { 518 return (ENOMEM); 519 } 520 } 521 522 if (dp->removable) { 523 if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) { 524 return (ENOMEM); 525 } 526 527 if (nvlist_add_boolean(attrs, DM_LOADED) != 0) { 528 return (ENOMEM); 529 } 530 } 531 532 if (nvlist_add_uint64(attrs, DM_SIZE, minfo.dki_capacity) != 0) { 533 return (ENOMEM); 534 } 535 536 if (nvlist_add_uint32(attrs, DM_BLOCKSIZE, minfo.dki_lbsize) != 0) { 537 return (ENOMEM); 538 } 539 540 if (nvlist_add_uint32(attrs, DM_MTYPE, 541 get_media_type(minfo.dki_media_type)) != 0) { 542 return (ENOMEM); 543 } 544 545 /* only for disks < 1TB */ 546 if (ioctl(fd, DKIOCGGEOM, &geometry) >= 0) { 547 struct vtoc vtoc; 548 549 if (nvlist_add_uint64(attrs, DM_START, 0) != 0) { 550 return (ENOMEM); 551 } 552 if (nvlist_add_uint64(attrs, DM_NACCESSIBLE, 553 geometry.dkg_ncyl * geometry.dkg_nhead * geometry.dkg_nsect) 554 != 0) { 555 return (ENOMEM); 556 } 557 if (nvlist_add_uint32(attrs, DM_NCYLINDERS, geometry.dkg_ncyl) 558 != 0) { 559 return (ENOMEM); 560 } 561 if (nvlist_add_uint32(attrs, DM_NPHYSCYLINDERS, geometry.dkg_pcyl) 562 != 0) { 563 return (ENOMEM); 564 } 565 if (nvlist_add_uint32(attrs, DM_NALTCYLINDERS, geometry.dkg_acyl) 566 != 0) { 567 return (ENOMEM); 568 } 569 if (nvlist_add_uint32(attrs, DM_NHEADS, geometry.dkg_nhead) != 0) { 570 return (ENOMEM); 571 } 572 if (nvlist_add_uint32(attrs, DM_NSECTORS, geometry.dkg_nsect) 573 != 0) { 574 return (ENOMEM); 575 } 576 577 if (read_vtoc(fd, &vtoc) >= 0 && vtoc.v_volume[0] != 0) { 578 char label[LEN_DKL_VVOL + 1]; 579 580 (void) snprintf(label, sizeof (label), "%.*s", LEN_DKL_VVOL, 581 vtoc.v_volume); 582 if (nvlist_add_string(attrs, DM_LABEL, label) != 0) { 583 return (ENOMEM); 584 } 585 } 586 587 } else { 588 /* check for disks > 1TB for accessible size */ 589 struct dk_gpt *efip; 590 591 if (efi_alloc_and_read(fd, &efip) >= 0) { 592 diskaddr_t p8size = 0; 593 594 if (nvlist_add_boolean(attrs, DM_EFI) != 0) { 595 return (ENOMEM); 596 } 597 if (nvlist_add_uint64(attrs, DM_START, efip->efi_first_u_lba) 598 != 0) { 599 return (ENOMEM); 600 } 601 /* partition 8 is reserved on EFI labels */ 602 if (efip->efi_nparts >= 9) { 603 p8size = efip->efi_parts[8].p_size; 604 } 605 if (nvlist_add_uint64(attrs, DM_NACCESSIBLE, 606 (efip->efi_last_u_lba - p8size) - efip->efi_first_u_lba) 607 != 0) { 608 efi_free(efip); 609 return (ENOMEM); 610 } 611 efi_free(efip); 612 } 613 } 614 615 /* This ioctl seems to be mainly for intel-based drives. */ 616 if (ioctl(fd, DKIOCG_PHYGEOM, &geometry) >= 0) { 617 if (nvlist_add_uint32(attrs, DM_NACTUALCYLINDERS, geometry.dkg_ncyl) 618 != 0) { 619 return (ENOMEM); 620 } 621 } 622 623 return (0); 624 } 625 626 static int 627 get_media_type(uint_t media_type) 628 { 629 switch (media_type) { 630 case DK_UNKNOWN: 631 return (DM_MT_UNKNOWN); 632 case DK_MO_ERASABLE: 633 return (DM_MT_MO_ERASABLE); 634 case DK_MO_WRITEONCE: 635 return (DM_MT_MO_WRITEONCE); 636 case DK_AS_MO: 637 return (DM_MT_AS_MO); 638 case DK_CDROM: 639 return (DM_MT_CDROM); 640 case DK_CDR: 641 return (DM_MT_CDR); 642 case DK_CDRW: 643 return (DM_MT_CDRW); 644 case DK_DVDROM: 645 return (DM_MT_DVDROM); 646 case DK_DVDR: 647 return (DM_MT_DVDR); 648 case DK_DVDRAM: 649 return (DM_MT_DVDRAM); 650 case DK_FIXED_DISK: 651 return (DM_MT_FIXED); 652 case DK_FLOPPY: 653 return (DM_MT_FLOPPY); 654 case DK_ZIP: 655 return (DM_MT_ZIP); 656 case DK_JAZ: 657 return (DM_MT_JAZ); 658 default: 659 return (DM_MT_UNKNOWN); 660 } 661 } 662 663 /* 664 * This function handles removable media not under volume management. 665 */ 666 static int 667 get_non_volm_name(disk_t *dp, char *mname, int size) 668 { 669 int loaded; 670 int fd; 671 672 loaded = 0; 673 674 if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { 675 struct dk_minfo minfo; 676 677 if ((loaded = media_read_info(fd, &minfo))) { 678 struct vtoc vtoc; 679 680 if (read_vtoc(fd, &vtoc) >= 0) { 681 if (vtoc.v_volume[0] != NULL) { 682 if (LEN_DKL_VVOL < size) { 683 (void) strlcpy(mname, vtoc.v_volume, LEN_DKL_VVOL); 684 } else { 685 (void) strlcpy(mname, vtoc.v_volume, size); 686 } 687 } 688 } 689 } 690 691 (void) close(fd); 692 } 693 694 return (loaded); 695 } 696