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