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