xref: /illumos-gate/usr/src/cmd/cdrw/device.c (revision 43a291055ab3951f6372241323fd4e2486098fff)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <volmgt.h>
32 #include <errno.h>
33 #include <sys/stat.h>
34 #include <sys/dkio.h>
35 #include <unistd.h>
36 #include <dirent.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <libintl.h>
40 #include <limits.h>
41 
42 #include "transport.h"
43 #include "mmc.h"
44 #include "device.h"
45 #include "util.h"
46 #include "msgs.h"
47 #include "misc_scsi.h"
48 #include "toshiba.h"
49 #include "main.h"
50 
51 /*
52  * Old sun drives have a vendor specific mode page for setting/getting speed.
53  * Also they use a different method for extracting audio.
54  * We have the device inquiry strings at this time. This is used to enable
55  * us to use older sun drives to extract audio.
56  */
57 static int
58 is_old_sun_drive(cd_device *dev)
59 {
60 	/*
61 	 * If we have a SONY CDU 561, CDU 8012, or TOSHIBA model with XMa we
62 	 * need to handle these drives a bit differently.
63 	 */
64 	if (strncmp("SONY", (const char *)&dev->d_inq[8], 4) == 0) {
65 		if (strncmp("CDU 561", (const char *)&dev->d_inq[16], 7) == 0)
66 			return (1);
67 		if (strncmp("CDU-8012", (const char *)&dev->d_inq[16], 8) == 0)
68 			return (1);
69 	}
70 
71 	if ((strncmp("TOSHIBA", (const char *)&dev->d_inq[8], 7) == 0) &&
72 	    (strncmp("XM", (const char *)&dev->d_inq[16], 2) == 0)) {
73 
74 		char product_id[17];
75 
76 		/* Changing speed is not allowed for 32X TOSHIBA drives */
77 		if (strncmp("SUN32XCD", (const char *)&dev->d_inq[24], 8) == 0)
78 			dev->d_cap |= DEV_CAP_SETTING_SPEED_NOT_ALLOWED;
79 		(void) strncpy(product_id, (const char *)&dev->d_inq[16], 16);
80 		product_id[16] = 0;
81 		if (strstr(product_id, "SUN") != NULL)
82 			return (1);
83 	}
84 	return (0);
85 }
86 
87 /*
88  * returns a cd_device handle for a node returned by lookup_device()
89  * also takes the user supplied name and stores it inside the node
90  */
91 cd_device *
92 get_device(char *user_supplied, char *node)
93 {
94 	cd_device *dev;
95 	int fd;
96 	uchar_t *cap;
97 	char devnode[PATH_MAX];
98 	int size;
99 	struct dk_minfo mediainfo;
100 	int use_cd_speed = 0;
101 
102 	/*
103 	 * we need to resolve any link paths to avoid fake files
104 	 * such as /dev/rdsk/../../export/file.
105 	 */
106 
107 	TRACE(traceall_msg("get_device(%s, %s)\n", user_supplied ?
108 	    user_supplied : "<nil>", node ? node : "<nil>"));
109 
110 	size = resolvepath(node, devnode, PATH_MAX);
111 	if ((size <= 0) || (size >= (PATH_MAX - 1)))
112 		return (NULL);
113 
114 	/* resolvepath may not return a null terminated string */
115 	devnode[size] = '\0';
116 
117 
118 	/* the device node must be in /devices/ or /vol/dev/rdsk */
119 
120 	if ((strncmp(devnode, "/devices/", 9) != 0) &&
121 	    (strncmp(devnode, "/vol/dev/rdsk", 13) != 0))
122 		return (NULL);
123 	/*
124 	 * Since we are currently running with the user euid it is
125 	 * safe to try to open the file without checking access.
126 	 */
127 
128 	fd = open(devnode, O_RDONLY|O_NDELAY);
129 
130 	if (fd < 0) {
131 		TRACE(traceall_msg("Cannot open %s: %s\n", node,
132 		    strerror(errno)));
133 		return (NULL);
134 	}
135 
136 	dev = (cd_device *)my_zalloc(sizeof (cd_device));
137 
138 	dev->d_node = (char *)my_zalloc(strlen(devnode) + 1);
139 	(void) strcpy(dev->d_node, devnode);
140 
141 	dev->d_fd = fd;
142 
143 	dev->d_inq = (uchar_t *)my_zalloc(INQUIRY_DATA_LENGTH);
144 
145 	if (!inquiry(fd, dev->d_inq)) {
146 		TRACE(traceall_msg("Inquiry failed on device %s\n", node));
147 		if (debug) {
148 			(void) printf("USCSI ioctl failed %d\n",
149 			    uscsi_error);
150 		}
151 		free(dev->d_inq);
152 		free(dev->d_node);
153 		(void) close(dev->d_fd);
154 		free(dev);
155 		return (NULL);
156 	}
157 
158 	if (debug) {
159 		cap = (uchar_t *)my_zalloc(18);
160 		(void) printf("Checking device type\n");
161 		if (get_mode_page(fd, 0x2A, 0, 8, cap)) {
162 			if (cap[2] & 0x10)
163 				(void) printf("DVD-R read support\n");
164 			if (cap[3] & 0x10)
165 				(void) printf("DVD-R write support\n");
166 			if (cap[5] & 0x4)
167 				(void) printf("R-W supported\n");
168 			if (cap[2] & 0x20)
169 				(void) printf("DVD-RAM read supported\n");
170 			if (cap[3] & 0x20)
171 				(void) printf("DVD-RAM write supported\n");
172 		} else {
173 			(void) printf("Could not read mode page 2A! \n");
174 		}
175 		free(cap);
176 	}
177 
178 	/* Detect if it's a Lite-ON drive with a streaming CD problem */
179 	if ((strncmp("LITE-ON", (const char *)&dev->d_inq[8], 7) == 0) &&
180 	    (strncmp("LTR-48", (const char *)&dev->d_inq[16], 6) == 0)) {
181 		use_cd_speed = 1;
182 	}
183 
184 	cap = (uchar_t *)my_zalloc(8);
185 	if (is_old_sun_drive(dev)) {
186 		dev->d_read_audio = toshiba_read_audio;
187 		dev->d_speed_ctrl = toshiba_speed_ctrl;
188 	} else if (use_cd_speed)  {
189 		/*
190 		 * Work around for Lite-on FW bug in which rt_speed_ctrl
191 		 * doesn't work correctly.
192 		 */
193 		dev->d_speed_ctrl = cd_speed_ctrl;
194 	} else {
195 		/*
196 		 * If feature 8 (see GET CONF MMC command) is supported,
197 		 * READ CD will work and will return jitter free audio data.
198 		 * Otherwise look at page code 2A for this info.
199 		 */
200 		if (get_configuration(fd, 0x1E, 8, cap)) {
201 			dev->d_read_audio = read_audio_through_read_cd;
202 			dev->d_cap |= DEV_CAP_ACCURATE_CDDA;
203 		} else if (get_mode_page(fd, 0x2A, 0, 8, cap)) {
204 			if (cap[5] & 1) {
205 				dev->d_read_audio = read_audio_through_read_cd;
206 				if (cap[5] & 2)
207 					dev->d_cap |= DEV_CAP_ACCURATE_CDDA;
208 			}
209 		}
210 		/*
211 		 * If feature 0x0107 is suported then Real-time streaming
212 		 * commands can be used for speed control. Otherwise try
213 		 * SET CD SPEED.
214 		 */
215 		if (get_configuration(fd, 0x0107, 8, cap)) {
216 			dev->d_speed_ctrl = rt_streaming_ctrl;
217 			if (debug)
218 				(void) printf("using rt speed ctrl\n");
219 		} else {
220 			dev->d_speed_ctrl = cd_speed_ctrl;
221 			if (debug)
222 				(void) printf("using cd speed ctrl\n");
223 		}
224 	}
225 	if (dev->d_read_audio != NULL)
226 		dev->d_cap |= DEV_CAP_EXTRACT_CDDA;
227 
228 	dev->d_blksize = 0;
229 
230 	/*
231 	 * Find the block size of the device so we can translate
232 	 * the reads/writes to the device blocksize.
233 	 */
234 
235 	if (ioctl(fd, DKIOCGMEDIAINFO, &mediainfo) < 0) {
236 
237 		/*
238 		 * If DKIOCGMEDIAINFO fails we'll try to get
239 		 * the blocksize from the device itself.
240 		 */
241 
242 		if (debug)
243 			(void) printf("DKIOCGMEDIAINFO failed\n");
244 
245 		if (read_capacity(fd, cap))
246 			dev->d_blksize = read_scsi32(cap + 4);
247 	} else {
248 
249 		dev->d_blksize = mediainfo.dki_lbsize;
250 	}
251 
252 	if (debug) {
253 		uint_t bsize;
254 
255 		(void) printf("blocksize = %d\n", dev->d_blksize);
256 		(void) printf("read_format_capacity = %d \n",
257 		    read_format_capacity(fd, &bsize));
258 	}
259 
260 	/*
261 	 * Some devices will return invalid blocksizes. ie. Toshiba
262 	 * drives will return 2352 when an audio CD is inserted.
263 	 * Older Sun drives will use 512 byte block sizes. All newer
264 	 * drives should have 2k blocksizes.
265 	 */
266 	if (((dev->d_blksize != 512) && (dev->d_blksize != 2048))) {
267 			if (is_old_sun_drive(dev)) {
268 				dev->d_blksize = 512;
269 			} else {
270 				dev->d_blksize = 2048;
271 			}
272 		if (debug)
273 			(void) printf(" switching to %d\n", dev->d_blksize);
274 	}
275 
276 	free(cap);
277 	if (user_supplied) {
278 		dev->d_name = (char *)my_zalloc(strlen(user_supplied) + 1);
279 		(void) strcpy(dev->d_name, user_supplied);
280 	}
281 	TRACE(traceall_msg("Got device %s\n", node));
282 	return (dev);
283 }
284 
285 void
286 fini_device(cd_device *dev)
287 {
288 	free(dev->d_inq);
289 	free(dev->d_node);
290 	(void) close(dev->d_fd);
291 	if (dev->d_name)
292 		free(dev->d_name);
293 	free(dev);
294 }
295 
296 static int
297 vol_name_to_dev_node(char *vname, char *found)
298 {
299 	struct stat statbuf;
300 	char *p1;
301 	int i;
302 
303 	if (vname == NULL)
304 		return (0);
305 	if (vol_running)
306 		(void) volmgt_check(vname);
307 	p1 = media_findname(vname);
308 	if (p1 == NULL)
309 		return (0);
310 	if (stat(p1, &statbuf) < 0) {
311 		free(p1);
312 		return (0);
313 	}
314 	if (statbuf.st_mode & S_IFDIR) {
315 		for (i = 0; i < 16; i++) {
316 			(void) snprintf(found, PATH_MAX, "%s/s%d", p1, i);
317 			if (access(found, F_OK) >= 0)
318 				break;
319 		}
320 		if (i == 16) {
321 			free(p1);
322 			return (0);
323 		}
324 	} else {
325 		(void) strlcpy(found, p1, PATH_MAX);
326 	}
327 	free(p1);
328 	return (1);
329 }
330 
331 /*
332  * Searches for volume manager's equivalent char device for the
333  * supplied pathname which is of the form of /dev/rdsk/cxtxdxsx
334  */
335 static int
336 vol_lookup(char *supplied, char *found)
337 {
338 	char tmpstr[PATH_MAX], tmpstr1[PATH_MAX], *p;
339 	int i, ret;
340 
341 	(void) strlcpy(tmpstr, supplied, PATH_MAX);
342 	if ((p = volmgt_symname(tmpstr)) == NULL) {
343 		if (strrchr(tmpstr, 's') == NULL)
344 			return (0);
345 		*((char *)(strrchr(tmpstr, 's') + 1)) = 0;
346 		for (i = 0; i < 16; i++) {
347 			(void) snprintf(tmpstr1, PATH_MAX, "%s%d", tmpstr, i);
348 			if ((p = volmgt_symname(tmpstr1)) != NULL)
349 				break;
350 		}
351 		if (p == NULL)
352 			return (0);
353 	}
354 	ret = vol_name_to_dev_node(p, found);
355 	free(p);
356 	return (ret);
357 }
358 
359 /*
360  * Builds an open()able device path from a user supplied node which can be
361  * of the * form of /dev/[r]dsk/cxtxdx[sx] or cxtxdx[sx] or volmgt-name like
362  * cdrom[n]
363  * returns the path found in 'found' and returns 1. Otherwise returns 0.
364  */
365 int
366 lookup_device(char *supplied, char *found)
367 {
368 	struct stat statbuf;
369 	int fd;
370 	char tmpstr[PATH_MAX];
371 
372 	/* If everything is fine and proper, no need to analyze */
373 	if ((stat(supplied, &statbuf) == 0) && (statbuf.st_mode & S_IFCHR) &&
374 	    ((fd = open(supplied, O_RDONLY|O_NDELAY)) >= 0)) {
375 		(void) close(fd);
376 		(void) strlcpy(found, supplied, PATH_MAX);
377 		return (1);
378 	}
379 	if (strncmp(supplied, "/dev/rdsk/", 10) == 0)
380 		return (vol_lookup(supplied, found));
381 	if (strncmp(supplied, "/dev/dsk/", 9) == 0) {
382 		(void) snprintf(tmpstr, PATH_MAX, "/dev/rdsk/%s",
383 		    (char *)strrchr(supplied, '/'));
384 
385 		if ((fd = open(tmpstr, O_RDONLY|O_NDELAY)) >= 0) {
386 			(void) close(fd);
387 			(void) strlcpy(found, supplied, PATH_MAX);
388 			return (1);
389 		}
390 		if ((access(tmpstr, F_OK) == 0) && vol_running)
391 			return (vol_lookup(tmpstr, found));
392 		else
393 			return (0);
394 	}
395 	if ((strncmp(supplied, "cdrom", 5) != 0) &&
396 	    (strlen(supplied) < 32)) {
397 		(void) snprintf(tmpstr, sizeof (tmpstr), "/dev/rdsk/%s",
398 		    supplied);
399 		if (access(tmpstr, F_OK) < 0) {
400 			(void) strcat(tmpstr, "s2");
401 		}
402 		if ((fd = open(tmpstr, O_RDONLY|O_NDELAY)) >= 0) {
403 			(void) close(fd);
404 			(void) strlcpy(found, tmpstr, PATH_MAX);
405 			return (1);
406 		}
407 		if ((access(tmpstr, F_OK) == 0) && vol_running)
408 			return (vol_lookup(tmpstr, found));
409 	}
410 	return (vol_name_to_dev_node(supplied, found));
411 }
412 
413 /*
414  * Opens the device node name passed and returns 1 (true) if the
415  * device is a CD.
416  */
417 
418 static int
419 is_cd(char *node)
420 {
421 	int fd;
422 	struct dk_cinfo cinfo;
423 	int ret = 1;
424 
425 	fd = open(node, O_RDONLY|O_NDELAY);
426 	if (fd < 0) {
427 		ret = 0;
428 	} else if (ioctl(fd, DKIOCINFO, &cinfo) < 0) {
429 		ret = 0;
430 	} else if (cinfo.dki_ctype != DKC_CDROM) {
431 		ret = 0;
432 	}
433 
434 	if (fd >= 0) {
435 		(void) close(fd);
436 	}
437 	return (ret);
438 }
439 
440 static void
441 print_header(void)
442 {
443 	/* l10n_NOTE : Column spacing should be kept same */
444 	(void) printf(gettext("    Node	           Connected Device"));
445 	/* l10n_NOTE : Column spacing should be kept same */
446 	(void) printf(gettext("	           Device type\n"));
447 	(void) printf(
448 	    "----------------------+--------------------------------");
449 	(void) printf("+-----------------\n");
450 }
451 
452 /*
453  * returns the number of writers or CD/DVD-roms found and the path of
454  * the first device found depending on the mode argument.
455  * possible mode values are:
456  * SCAN_ALL_CDS 	Scan all CD/DVD devices. Return first CD-RW found.
457  * SCAN_WRITERS		Scan all CD-RW devices. Return first one found.
458  * SCAN_LISTDEVS	List all devices found.
459  */
460 int
461 scan_for_cd_device(int mode, cd_device **found)
462 {
463 	DIR *dir;
464 	struct dirent *dirent;
465 	char sdev[PATH_MAX], dev[PATH_MAX];
466 	cd_device *t_dev;
467 	int writers_found = 0;
468 	int header_printed = 0;
469 	int is_writer;
470 	int retry = 0;
471 	int total_devices_found;
472 	int total_devices_found_last_time = 0;
473 	int defer = 0;
474 
475 #define	MAX_RETRIES_FOR_SCANNING 5
476 
477 	TRACE(traceall_msg("scan_for_cd_devices (mode=%d) called\n", mode));
478 
479 	if (mode) {
480 		if (vol_running)
481 			defer = 1;
482 		(void) printf(gettext("Looking for CD devices...\n"));
483 	}
484 try_again:
485 
486 	dir = opendir("/dev/rdsk");
487 	if (dir == NULL)
488 		return (0);
489 
490 	writers_found = 0;
491 	total_devices_found = 0;
492 	while ((dirent = readdir(dir)) != NULL) {
493 		if (dirent->d_name[0] == '.')
494 			continue;
495 		(void) snprintf(sdev, PATH_MAX, "/dev/rdsk/%s",
496 		    dirent->d_name);
497 		if (strcmp("s2", (char *)strrchr(sdev, 's')) != 0)
498 			continue;
499 		if (!lookup_device(sdev, dev))
500 			continue;
501 		if (!is_cd(dev))
502 			continue;
503 		if ((t_dev = get_device(NULL, dev)) == NULL) {
504 			continue;
505 		}
506 		total_devices_found++;
507 
508 		is_writer = !(check_device(t_dev, CHECK_DEVICE_NOT_WRITABLE));
509 
510 		if (is_writer) {
511 			writers_found++;
512 
513 			if ((writers_found == 1) && (mode != SCAN_LISTDEVS)) {
514 				*found = t_dev;
515 			}
516 
517 		} else if ((mode == SCAN_ALL_CDS) && (writers_found == 0) &&
518 		    (total_devices_found == 1) && found) {
519 
520 			/* We found a CD-ROM or DVD-ROM */
521 			*found = t_dev;
522 		}
523 
524 		if ((mode == SCAN_LISTDEVS) && (!defer)) {
525 			char *sn;
526 
527 			sn = volmgt_symname(sdev);
528 			if (!header_printed) {
529 				print_header();
530 				header_printed = 1;
531 			}
532 			/* show vendor, model, firmware rev and device type */
533 			(void) printf(" %-21.21s| %.8s %.16s %.4s | %s%s\n",
534 			    sn ? sn : sdev, &t_dev->d_inq[8],
535 			    &t_dev->d_inq[16], &t_dev->d_inq[32],
536 			    gettext("CD Reader"),
537 			    is_writer ? gettext("/Writer") : "");
538 			if (sn)
539 				free(sn);
540 		}
541 		if ((found != NULL) && ((*found) != t_dev))
542 			fini_device(t_dev);
543 	}
544 
545 	(void) closedir(dir);
546 
547 
548 	/*
549 	 * If volume manager is running we'll do a retry in case the
550 	 * user has just inserted media, This should allow vold
551 	 * enough time to create the device links. If we cannot
552 	 * find any devices, or we find any new devices we'll retry
553 	 * again up to MAX_RETRIES_FOR_SCANNING
554 	 */
555 
556 	retry++;
557 
558 	if ((retry <= MAX_RETRIES_FOR_SCANNING) && vol_running) {
559 		if (defer || ((mode != SCAN_LISTDEVS) &&
560 		    (writers_found == 0))) {
561 
562 			if ((total_devices_found == 0) ||
563 			    (total_devices_found !=
564 			    total_devices_found_last_time)) {
565 
566 				/* before we rescan remove the device found */
567 				if (total_devices_found != 0 && t_dev) {
568 					fini_device(t_dev);
569 				}
570 
571 				total_devices_found_last_time =
572 				    total_devices_found;
573 					(void) sleep(2);
574 					goto try_again;
575 			} else {
576 
577 				/* Do the printing this time */
578 				defer = 0;
579 				goto try_again;
580 
581 			}
582 
583 		}
584 	}
585 	if ((mode & SCAN_WRITERS) || writers_found)
586 		return (writers_found);
587 	else
588 		return (total_devices_found);
589 }
590 
591 /*
592  * Check device for various conditions/capabilities
593  * If EXIT_IF_CHECK_FAILED set in cond then it will also exit after
594  * printing a message.
595  */
596 int
597 check_device(cd_device *dev, int cond)
598 {
599 	uchar_t *disc_info, disc_status = 0, erasable = 0;
600 	uchar_t page_code[4];
601 	char *errmsg = NULL;
602 
603 	if ((errmsg == NULL) && (cond & CHECK_TYPE_NOT_CDROM) &&
604 	    ((dev->d_inq[0] & 0x1f) != 5)) {
605 		errmsg =
606 		    gettext("Specified device does not appear to be a CDROM");
607 	}
608 
609 	if ((errmsg == NULL) && (cond & CHECK_DEVICE_NOT_READY) &&
610 	    !test_unit_ready(dev->d_fd)) {
611 		errmsg = gettext("Device not ready");
612 	}
613 
614 	/* Look at the capabilities page for this information */
615 	if ((errmsg == NULL) && (cond & CHECK_DEVICE_NOT_WRITABLE)) {
616 		if (!get_mode_page(dev->d_fd, 0x2a, 0, 4, page_code) ||
617 		    ((page_code[3] & 1) == 0)) {
618 			errmsg = gettext("Target device is not a CD writer");
619 		}
620 	}
621 
622 	if ((errmsg == NULL) && (cond & CHECK_NO_MEDIA)) {
623 		if (!test_unit_ready(dev->d_fd) && (uscsi_status == 2) &&
624 		    ((RQBUFLEN - rqresid) >= 14) &&
625 		    ((SENSE_KEY(rqbuf) & 0x0f) == 2) && (ASC(rqbuf) == 0x3A) &&
626 		    ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
627 		    (ASCQ(rqbuf) == 2))) {
628 			/* medium not present */
629 			errmsg = gettext("No media in device");
630 		}
631 	}
632 
633 
634 
635 	/* Issue READ DISC INFORMATION mmc command */
636 	if ((errmsg == NULL) && ((cond & CHECK_MEDIA_IS_NOT_BLANK) ||
637 	    (cond & CHECK_MEDIA_IS_NOT_WRITABLE) ||
638 	    (cond & CHECK_MEDIA_IS_NOT_ERASABLE))) {
639 
640 		disc_info = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
641 		if (!read_disc_info(dev->d_fd, disc_info)) {
642 			errmsg = gettext("Cannot obtain disc information");
643 		} else {
644 			disc_status = disc_info[2] & 0x03;
645 			erasable = disc_info[2] & 0x10;
646 		}
647 		free(disc_info);
648 		if (errmsg == NULL) {
649 			if (!erasable && (cond & CHECK_MEDIA_IS_NOT_ERASABLE))
650 				errmsg = gettext(
651 				    "Media in the device is not erasable");
652 			else if ((disc_status != 0) &&
653 			    (cond & CHECK_MEDIA_IS_NOT_BLANK))
654 				errmsg = gettext(
655 				    "Media in the device is not blank");
656 			else if ((disc_status == 2) &&
657 			    (cond & CHECK_MEDIA_IS_NOT_WRITABLE) &&
658 			    ((device_type != DVD_PLUS_W) &&
659 			    (device_type != DVD_PLUS)))
660 				errmsg = gettext(
661 				    "Media in the device is not writable");
662 		}
663 	}
664 
665 	if (errmsg) {
666 		if (cond & EXIT_IF_CHECK_FAILED) {
667 			err_msg("%s.\n", errmsg);
668 			exit(1);
669 		}
670 		return (1);
671 	}
672 	return (0);
673 }
674 
675 /*
676  * Generic routine for writing whatever the next track is and taking
677  * care of the progress bar. Mode tells the track type (audio or data).
678  * Data from track is taken from the byte stream h
679  */
680 void
681 write_next_track(int mode, bstreamhandle h)
682 {
683 	struct track_info *ti;
684 	struct trackio_error *te;
685 	off_t size;
686 
687 	ti = (struct track_info *)my_zalloc(sizeof (*ti));
688 	if ((build_track_info(target, -1, ti) == 0) ||
689 	    ((ti->ti_flags & TI_NWA_VALID) == 0)) {
690 		if ((device_type == DVD_PLUS) || (device_type ==
691 		    DVD_PLUS_W)) {
692 			ti->ti_flags |= TI_NWA_VALID;
693 		} else {
694 			err_msg(gettext(
695 			    "Cannot get writable address for the media.\n"));
696 			exit(1);
697 		}
698 	}
699 	if (ti->ti_nwa != ti->ti_start_address) {
700 		err_msg(gettext(
701 		    "Media state is not suitable for this write mode.\n"));
702 		exit(1);
703 	}
704 	if (mode == TRACK_MODE_DATA) {
705 		if (!(ti->ti_track_mode & 4)) {
706 			/* Write track depends upon this bit */
707 			ti->ti_track_mode |= TRACK_MODE_DATA;
708 		}
709 	}
710 	size = 0;
711 	h->bstr_size(h, &size);
712 	h->bstr_rewind(h);
713 	te = (struct trackio_error *)my_zalloc(sizeof (*te));
714 
715 	print_n_flush(gettext("Writing track %d..."), (int)ti->ti_track_no);
716 	init_progress();
717 	if (!write_track(target, ti, h, progress, (void *)size, te)) {
718 		if (te->err_type == TRACKIO_ERR_USER_ABORT) {
719 			(void) str_print(gettext("Aborted.\n"), progress_pos);
720 		} else {
721 			if (device_type != DVD_PLUS_W) {
722 			/* l10n_NOTE : 'failed' as in Writing Track...failed  */
723 				(void) str_print(gettext("failed.\n"),
724 				    progress_pos);
725 			}
726 		}
727 	}
728 	/* l10n_NOTE : 'done' as in "Writing track 1...done"  */
729 	(void) str_print(gettext("done.\n"), progress_pos);
730 	free(ti);
731 	free(te);
732 }
733 
734 void
735 list(void)
736 {
737 	if (scan_for_cd_device(SCAN_LISTDEVS, NULL) == 0) {
738 		if (vol_running) {
739 			err_msg(gettext(
740 			    "No CD writers found or no media in the drive.\n"));
741 		} else {
742 			if (cur_uid != 0) {
743 				err_msg(gettext(
744 				    "Volume manager is not running.\n"));
745 				err_msg(gettext(
746 "Please start volume manager or run cdrw as root to access all devices.\n"));
747 			} else {
748 				err_msg(gettext("No CD writers found.\n"));
749 			}
750 		}
751 	}
752 	exit(0);
753 }
754 
755 void
756 get_media_type(int fd)
757 {
758 	uchar_t cap[DVD_CONFIG_SIZE];
759 	uint_t i;
760 
761 
762 	(void) memset(cap, 0, DVD_CONFIG_SIZE);
763 	if (get_configuration(fd, 0, DVD_CONFIG_SIZE, cap)) {
764 		if (debug && verbose) {
765 			(void) printf("\nprofile = ");
766 
767 			for (i = 7; i < 0x20; i += 4)
768 				(void) printf(" 0x%x", cap[i]);
769 			(void) printf("\n");
770 		}
771 
772 		switch (cap[7]) {
773 			case 0x8: /* CD-ROM */
774 				if (debug)
775 					(void) printf("CD-ROM found\n");
776 				/*
777 				 * To avoid regression issues, treat as
778 				 * A cdrw, we will check the writable
779 				 * mode page to see if the media is
780 				 * actually writable.
781 				 */
782 				device_type = CD_RW;
783 				break;
784 
785 			case 0x9: /* CD-R */
786 				if (debug)
787 					(void) printf("CD-R found\n");
788 				device_type = CD_RW;
789 				break;
790 
791 			case 0x10: /* DVD-ROM */
792 				/*
793 				 * Have seen drives return DVD+RW media
794 				 * DVD-ROM, so try treating it as a DVD+RW
795 				 * profile. checking for writable media
796 				 * is done through mode page 5.
797 				 */
798 				if (debug)
799 					(void) printf("DVD-ROM found\n");
800 				device_type = DVD_PLUS_W;
801 				break;
802 
803 			case 0xA: /* CD-RW */
804 				if (debug)
805 					(void) printf("CD-RW found\n");
806 				device_type = CD_RW;
807 				break;
808 
809 			case 0x11: /* DVD-R */
810 				if (debug)
811 					(void) printf("DVD-R found\n");
812 				device_type = DVD_MINUS;
813 				break;
814 
815 			case 0x12: /* DVD-RAM */
816 				if (debug)
817 					(void) printf("DVD-RAM found\n");
818 				/* treat as CD-RW, may be a legacy drive */
819 				device_type = CD_RW;
820 				break;
821 
822 			case 0x13: /* DVD-RW restricted overwrite */
823 			case 0x14: /* DVD-RW sequencial */
824 				if (debug)
825 					(void) printf("DVD-RW found\n");
826 				device_type = DVD_MINUS;
827 				break;
828 
829 			case 0x1A: /* DVD+RW */
830 				if (debug)
831 					(void) printf("DVD+RW found\n");
832 
833 				device_type = DVD_PLUS_W;
834 				break;
835 			case 0x1B: /* DVD+R */
836 				if (debug)
837 					(void) printf("DVD+R found\n");
838 				device_type = DVD_PLUS;
839 				break;
840 
841 			default:
842 				if (debug)
843 					(void) printf(
844 					"unknown drive found\n type = 0x%x",
845 					cap[7]);
846 				/*
847 				 * Treat as CD_RW to avoid regression, may
848 				 * be a legacy drive.
849 				 */
850 				device_type = CD_RW;
851 		}
852 	}
853 }
854