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 **
media_get_assoc_descriptors(descriptor_t * desc,dm_desc_type_t type,int * errp)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 **
media_get_assocs(descriptor_t * dp,int * errp)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 *
media_get_attributes(descriptor_t * dp,int * errp)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 *
media_get_descriptor_by_name(char * name,int * errp)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 **
media_get_descriptors(int filter[],int * errp)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 *
media_get_name(descriptor_t * desc)203 media_get_name(descriptor_t *desc)
204 {
205 return (desc->name);
206 }
207
208 /* ARGSUSED */
209 nvlist_t *
media_get_stats(descriptor_t * dp,int stat_type,int * errp)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
media_make_descriptors()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
media_read_info(int fd,struct dk_minfo * minfo)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
media_read_name(disk_t * dp,char * mname,int size)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 **
apply_filter(descriptor_t ** media,int filter[],int * errp)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
desc_ok(descriptor_t * dp)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
get_attrs(disk_t * dp,int fd,nvlist_t * attrs)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