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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <fcntl.h> 29 #include <libdevinfo.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <stropts.h> 35 #include <sys/dkio.h> 36 #include <sys/sunddi.h> 37 #include <sys/types.h> 38 #include <unistd.h> 39 #include <sys/vtoc.h> 40 #include <sys/efi_partition.h> 41 42 #include "libdiskmgt.h" 43 #include "disks_private.h" 44 #include "partition.h" 45 46 #define IOCTLRETRIES 2 47 #define IOCTLRETRYINTERVAL 1 48 49 static descriptor_t **apply_filter(descriptor_t **media, int filter[], 50 int *errp); 51 static int get_attrs(disk_t *dp, int fd, nvlist_t *attrs); 52 static int get_rmm_name(disk_t *dp, char *mname, int size); 53 static int get_media_type(uint_t media_type); 54 static int desc_ok(descriptor_t *dp); 55 56 /* 57 * This function gets the descriptors we are associated with. 58 */ 59 descriptor_t ** 60 media_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, 61 int *errp) 62 { 63 if (!desc_ok(desc)) { 64 *errp = ENODEV; 65 return (NULL); 66 } 67 68 switch (type) { 69 case DM_DRIVE: 70 return (drive_get_assocs(desc, errp)); 71 case DM_PARTITION: 72 return (partition_get_assocs(desc, errp)); 73 case DM_SLICE: 74 return (slice_get_assocs(desc, errp)); 75 } 76 77 *errp = EINVAL; 78 return (NULL); 79 } 80 81 /* 82 * Get the media descriptors for the given drive/partition/slice. 83 */ 84 descriptor_t ** 85 media_get_assocs(descriptor_t *dp, int *errp) 86 { 87 descriptor_t **media; 88 char mname[MAXPATHLEN]; 89 90 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { 91 /* 92 * For drives, this means no media but slice/part. 93 * require media. 94 */ 95 if (dp->type == DM_DRIVE) { 96 return (libdiskmgt_empty_desc_array(errp)); 97 } else { 98 *errp = ENODEV; 99 return (NULL); 100 } 101 } 102 103 /* make the snapshot */ 104 media = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); 105 if (media == NULL) { 106 *errp = ENOMEM; 107 return (NULL); 108 } 109 110 media[0] = cache_get_desc(DM_MEDIA, dp->p.disk, mname, NULL, errp); 111 if (*errp != 0) { 112 free(media); 113 return (NULL); 114 } 115 media[1] = NULL; 116 117 *errp = 0; 118 return (media); 119 } 120 121 nvlist_t * 122 media_get_attributes(descriptor_t *dp, int *errp) 123 { 124 nvlist_t *attrs = NULL; 125 int fd; 126 127 if (!desc_ok(dp)) { 128 *errp = ENODEV; 129 return (NULL); 130 } 131 132 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { 133 *errp = ENOMEM; 134 return (NULL); 135 } 136 137 fd = drive_open_disk(dp->p.disk, NULL, 0); 138 139 if ((*errp = get_attrs(dp->p.disk, fd, attrs)) != 0) { 140 nvlist_free(attrs); 141 attrs = NULL; 142 } 143 144 if (fd >= 0) { 145 (void) close(fd); 146 } 147 148 return (attrs); 149 } 150 151 descriptor_t * 152 media_get_descriptor_by_name(char *name, int *errp) 153 { 154 descriptor_t **media; 155 int i; 156 descriptor_t *medium = NULL; 157 158 media = cache_get_descriptors(DM_MEDIA, errp); 159 if (*errp != 0) { 160 return (NULL); 161 } 162 163 for (i = 0; media[i]; i++) { 164 if (libdiskmgt_str_eq(name, media[i]->name)) { 165 medium = media[i]; 166 } else { 167 /* clean up the unused descriptors */ 168 cache_free_descriptor(media[i]); 169 } 170 } 171 free(media); 172 173 if (medium == NULL) { 174 *errp = ENODEV; 175 } 176 177 return (medium); 178 } 179 180 descriptor_t ** 181 media_get_descriptors(int filter[], int *errp) 182 { 183 descriptor_t **media; 184 185 media = cache_get_descriptors(DM_MEDIA, errp); 186 if (*errp != 0) { 187 return (NULL); 188 } 189 190 if (filter != NULL && filter[0] != DM_FILTER_END) { 191 descriptor_t **found; 192 193 found = apply_filter(media, filter, errp); 194 if (*errp != 0) { 195 media = NULL; 196 } else { 197 media = found; 198 } 199 } 200 201 return (media); 202 } 203 204 char * 205 media_get_name(descriptor_t *desc) 206 { 207 return (desc->name); 208 } 209 210 /* ARGSUSED */ 211 nvlist_t * 212 media_get_stats(descriptor_t *dp, int stat_type, int *errp) 213 { 214 /* There are no stat types defined for media */ 215 *errp = EINVAL; 216 return (NULL); 217 } 218 219 int 220 media_make_descriptors() 221 { 222 int error; 223 disk_t *dp; 224 char mname[MAXPATHLEN]; 225 226 dp = cache_get_disklist(); 227 while (dp != NULL) { 228 if (media_read_name(dp, mname, sizeof (mname))) { 229 cache_load_desc(DM_MEDIA, dp, mname, NULL, &error); 230 if (error != 0) { 231 return (error); 232 } 233 } 234 235 dp = dp->next; 236 } 237 238 return (0); 239 } 240 241 /* 242 * Read the media information. 243 */ 244 int 245 media_read_info(int fd, struct dk_minfo *minfo) 246 { 247 int status; 248 int tries = 0; 249 250 minfo->dki_media_type = 0; 251 252 /* 253 * This ioctl can fail if the media is not loaded or spun up. 254 * Retrying can sometimes succeed since the first ioctl will have 255 * started the media before the ioctl timed out so the media may be 256 * spun up on the subsequent attempt. 257 */ 258 while ((status = ioctl(fd, DKIOCGMEDIAINFO, minfo)) < 0) { 259 tries++; 260 if (tries >= IOCTLRETRIES) { 261 break; 262 } 263 (void) sleep(IOCTLRETRYINTERVAL); 264 } 265 266 if (status < 0) { 267 return (0); 268 } 269 270 return (1); 271 } 272 273 /* return 1 if there is media, 0 if not. */ 274 int 275 media_read_name(disk_t *dp, char *mname, int size) 276 { 277 mname[0] = 0; 278 279 if (!dp->removable) { 280 /* not removable, so media name is devid */ 281 if (dp->device_id != NULL) { 282 (void) strlcpy(mname, dp->device_id, size); 283 } 284 return (1); 285 } 286 287 /* This is a removable media drive. */ 288 return (get_rmm_name(dp, mname, size)); 289 } 290 291 static descriptor_t ** 292 apply_filter(descriptor_t **media, int filter[], int *errp) 293 { 294 descriptor_t **found; 295 int i; 296 int cnt = 0; 297 int pos; 298 299 /* count the number of media in the snapshot */ 300 for (i = 0; media[i]; i++) { 301 cnt++; 302 } 303 304 found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 305 if (found == NULL) { 306 *errp = ENOMEM; 307 cache_free_descriptors(media); 308 return (NULL); 309 } 310 311 pos = 0; 312 for (i = 0; media[i]; i++) { 313 int fd; 314 struct dk_minfo minfo; 315 316 if ((fd = drive_open_disk(media[i]->p.disk, NULL, 0)) < 0) { 317 continue; 318 } 319 320 if (media_read_info(fd, &minfo)) { 321 int mtype; 322 int j; 323 int match; 324 325 mtype = get_media_type(minfo.dki_media_type); 326 327 match = 0; 328 for (j = 0; filter[j] != DM_FILTER_END; j++) { 329 if (mtype == filter[j]) { 330 found[pos++] = media[i]; 331 match = 1; 332 break; 333 } 334 } 335 336 if (!match) { 337 cache_free_descriptor(media[i]); 338 } 339 } 340 (void) close(fd); 341 } 342 found[pos] = NULL; 343 free(media); 344 345 *errp = 0; 346 return (found); 347 } 348 349 /* return 1 if the media descriptor is still valid, 0 if not. */ 350 static int 351 desc_ok(descriptor_t *dp) 352 { 353 /* First verify the media name for removable media */ 354 if (dp->p.disk->removable) { 355 char mname[MAXPATHLEN]; 356 357 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { 358 return (0); 359 } 360 361 if (mname[0] == 0) { 362 return (libdiskmgt_str_eq(dp->name, NULL)); 363 } else { 364 return (libdiskmgt_str_eq(dp->name, mname)); 365 } 366 } 367 368 return (1); 369 } 370 371 static int 372 get_attrs(disk_t *dp, int fd, nvlist_t *attrs) 373 { 374 struct dk_minfo minfo; 375 struct dk_geom geometry; 376 377 if (fd < 0) { 378 return (ENODEV); 379 } 380 381 bzero(&minfo, sizeof (struct dk_minfo)); 382 383 /* The first thing to do is read the media */ 384 if (!media_read_info(fd, &minfo)) { 385 return (ENODEV); 386 } 387 388 if (partition_has_fdisk(dp, fd)) { 389 if (nvlist_add_boolean(attrs, DM_FDISK) != 0) { 390 return (ENOMEM); 391 } 392 } 393 394 if (dp->removable) { 395 if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) { 396 return (ENOMEM); 397 } 398 399 if (nvlist_add_boolean(attrs, DM_LOADED) != 0) { 400 return (ENOMEM); 401 } 402 } 403 404 if (nvlist_add_uint64(attrs, DM_SIZE, minfo.dki_capacity) != 0) { 405 return (ENOMEM); 406 } 407 408 if (nvlist_add_uint32(attrs, DM_BLOCKSIZE, minfo.dki_lbsize) != 0) { 409 return (ENOMEM); 410 } 411 412 if (nvlist_add_uint32(attrs, DM_MTYPE, 413 get_media_type(minfo.dki_media_type)) != 0) { 414 return (ENOMEM); 415 } 416 417 /* only for disks < 1TB and x86 */ 418 #if defined(i386) || defined(__amd64) 419 if (ioctl(fd, DKIOCG_PHYGEOM, &geometry) >= 0) { 420 #else 421 /* sparc call */ 422 if (ioctl(fd, DKIOCGGEOM, &geometry) >= 0) { 423 #endif 424 struct vtoc vtoc; 425 426 if (nvlist_add_uint64(attrs, DM_START, 0) != 0) { 427 return (ENOMEM); 428 } 429 if (nvlist_add_uint64(attrs, DM_NACCESSIBLE, 430 geometry.dkg_ncyl * geometry.dkg_nhead * geometry.dkg_nsect) 431 != 0) { 432 return (ENOMEM); 433 } 434 if (nvlist_add_uint32(attrs, DM_NCYLINDERS, geometry.dkg_ncyl) 435 != 0) { 436 return (ENOMEM); 437 } 438 if (nvlist_add_uint32(attrs, DM_NPHYSCYLINDERS, 439 geometry.dkg_pcyl) != 0) { 440 return (ENOMEM); 441 } 442 if (nvlist_add_uint32(attrs, DM_NALTCYLINDERS, 443 geometry.dkg_acyl) != 0) { 444 return (ENOMEM); 445 } 446 if (nvlist_add_uint32(attrs, DM_NHEADS, 447 geometry.dkg_nhead) != 0) { 448 return (ENOMEM); 449 } 450 if (nvlist_add_uint32(attrs, DM_NSECTORS, geometry.dkg_nsect) 451 != 0) { 452 return (ENOMEM); 453 } 454 if (nvlist_add_uint32(attrs, DM_NACTUALCYLINDERS, 455 geometry.dkg_ncyl) != 0) { 456 return (ENOMEM); 457 } 458 459 if (read_vtoc(fd, &vtoc) >= 0 && vtoc.v_volume[0] != 0) { 460 char label[LEN_DKL_VVOL + 1]; 461 462 (void) snprintf(label, sizeof (label), "%.*s", 463 LEN_DKL_VVOL, vtoc.v_volume); 464 if (nvlist_add_string(attrs, DM_LABEL, label) != 0) { 465 return (ENOMEM); 466 } 467 } 468 469 } else { 470 /* check for disks > 1TB for accessible size */ 471 struct dk_gpt *efip; 472 473 if (efi_alloc_and_read(fd, &efip) >= 0) { 474 diskaddr_t p8size = 0; 475 476 if (nvlist_add_boolean(attrs, DM_EFI) != 0) { 477 return (ENOMEM); 478 } 479 if (nvlist_add_uint64(attrs, DM_START, 480 efip->efi_first_u_lba) != 0) { 481 return (ENOMEM); 482 } 483 /* partition 8 is reserved on EFI labels */ 484 if (efip->efi_nparts >= 9) { 485 p8size = efip->efi_parts[8].p_size; 486 } 487 if (nvlist_add_uint64(attrs, DM_NACCESSIBLE, 488 (efip->efi_last_u_lba - p8size) - 489 efip->efi_first_u_lba) != 0) { 490 efi_free(efip); 491 return (ENOMEM); 492 } 493 efi_free(efip); 494 } 495 } 496 return (0); 497 } 498 499 static int 500 get_media_type(uint_t media_type) 501 { 502 switch (media_type) { 503 case DK_UNKNOWN: 504 return (DM_MT_UNKNOWN); 505 case DK_MO_ERASABLE: 506 return (DM_MT_MO_ERASABLE); 507 case DK_MO_WRITEONCE: 508 return (DM_MT_MO_WRITEONCE); 509 case DK_AS_MO: 510 return (DM_MT_AS_MO); 511 case DK_CDROM: 512 return (DM_MT_CDROM); 513 case DK_CDR: 514 return (DM_MT_CDR); 515 case DK_CDRW: 516 return (DM_MT_CDRW); 517 case DK_DVDROM: 518 return (DM_MT_DVDROM); 519 case DK_DVDR: 520 return (DM_MT_DVDR); 521 case DK_DVDRAM: 522 return (DM_MT_DVDRAM); 523 case DK_FIXED_DISK: 524 return (DM_MT_FIXED); 525 case DK_FLOPPY: 526 return (DM_MT_FLOPPY); 527 case DK_ZIP: 528 return (DM_MT_ZIP); 529 case DK_JAZ: 530 return (DM_MT_JAZ); 531 default: 532 return (DM_MT_UNKNOWN); 533 } 534 } 535 536 /* 537 * This function handles removable media. 538 */ 539 static int 540 get_rmm_name(disk_t *dp, char *mname, int size) 541 { 542 int loaded; 543 int fd; 544 545 loaded = 0; 546 547 if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { 548 struct dk_minfo minfo; 549 550 if ((loaded = media_read_info(fd, &minfo))) { 551 struct vtoc vtoc; 552 553 if (read_vtoc(fd, &vtoc) >= 0) { 554 if (vtoc.v_volume[0] != NULL) { 555 if (LEN_DKL_VVOL < size) { 556 (void) strlcpy(mname, 557 vtoc.v_volume, 558 LEN_DKL_VVOL); 559 } else { 560 (void) strlcpy(mname, 561 vtoc.v_volume, size); 562 } 563 } 564 } 565 } 566 567 (void) close(fd); 568 } 569 570 return (loaded); 571 } 572