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