summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cdrw/copycd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cdrw/copycd.c')
-rw-r--r--usr/src/cmd/cdrw/copycd.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/usr/src/cmd/cdrw/copycd.c b/usr/src/cmd/cdrw/copycd.c
new file mode 100644
index 0000000000..21bab5c87b
--- /dev/null
+++ b/usr/src/cmd/cdrw/copycd.c
@@ -0,0 +1,427 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#include "main.h"
+#include "util.h"
+#include "misc_scsi.h"
+#include "mmc.h"
+#include "bstream.h"
+#include "device.h"
+#include "msgs.h"
+#include "transport.h"
+
+struct t_data {
+ bstreamhandle h;
+ struct track_info ti;
+};
+
+int read_audio_track(cd_device *dev, struct track_info *ti, bstreamhandle h);
+
+#define READ_BURST 24 /* < 64K in all cases */
+
+/*
+ * This reads the data off of a cd while updating the progress indicator.
+ * We want to do this in smaller chunks since some CD drives have
+ * problems with larger reads.
+ */
+static int
+read_data_track(cd_device *dev, struct track_info *ti, bstreamhandle h)
+{
+ int blksize;
+ uint32_t blks_read, cblk, read_chunk, read_size;
+ uchar_t *buf;
+ int ret, sav;
+ int link_blks_count;
+
+ buf = NULL;
+ ret = 0;
+
+ /*
+ * the last link_blks_count blocks may not exist or be completely
+ * filled. We need to record the amount to avoid bailing out if
+ * they cannot be read.
+ */
+
+ if (dev->d_blksize == 512) {
+ blksize = 512;
+ link_blks_count = 8;
+ } else {
+ blksize = 2048;
+ link_blks_count = 2;
+ }
+
+ buf = (uchar_t *)my_zalloc(READ_BURST * blksize);
+
+ print_n_flush(gettext("Reading track %d..."), ti->ti_track_no);
+
+ if (verbose)
+ print_n_flush("Track size is %u...", ti->ti_track_size);
+
+ init_progress();
+ cblk = ti->ti_start_address;
+ blks_read = 0;
+ while (blks_read < ti->ti_track_size) {
+ /* Last few are special */
+ read_chunk = ti->ti_track_size - blks_read - link_blks_count;
+ read_chunk = (read_chunk > READ_BURST) ? READ_BURST :
+ read_chunk;
+ if (read_chunk == 0) {
+ /* Time for last link blocks */
+ read_chunk = link_blks_count;
+ }
+ read_size = read_chunk * blksize;
+ if (read10(dev->d_fd, cblk, read_chunk, buf, read_size)) {
+ if (h->bstr_write(h, buf, read_size) != read_size) {
+ goto read_data_track_failed;
+ }
+ } else {
+ if (blks_read !=
+ (ti->ti_track_size - link_blks_count)) {
+ goto read_data_track_failed;
+ } else {
+ /* Read can fail for last link sectors */
+ errno = 0;
+ }
+ }
+ blks_read += read_chunk;
+ cblk += read_chunk;
+ (void) progress((void *)(ti->ti_track_size), blks_read);
+ }
+ /* l10n_NOTE : 'done' as in "Reading track 1...done" */
+ (void) str_print(gettext("done.\n"), progress_pos);
+ ret = 1;
+read_data_track_failed:
+ sav = errno;
+
+ free(buf);
+ errno = sav;
+ return (ret);
+}
+
+static void
+ensure_media_space(uint32_t total_nblks, uchar_t end_tno)
+{
+ off_t nblks_avail;
+ uint_t bsize;
+
+ get_media_type(target->d_fd);
+
+ if (use_media_stated_capacity) {
+ nblks_avail = get_last_possible_lba(target);
+
+ if (nblks_avail <= 0) {
+
+ /* most newer drives use READ FORMAT CAPACITY */
+ nblks_avail = read_format_capacity(target->d_fd,
+ &bsize);
+
+ /* if both methods fail no choice but to bail out */
+ if (nblks_avail <= 0) {
+
+ err_msg(gettext(
+ "Cannot find out media capacity.\n"));
+ exit(1);
+ }
+ }
+ } else {
+ if (device_type == CD_RW) {
+ nblks_avail = MAX_CD_BLKS;
+ } else {
+ /*
+ * For DVD drives use read_format_capacity as default
+ * retrieve the media size, it can be 3.6, 3.9, 4.2,
+ * 4.7, or 9.2 GB
+ */
+ nblks_avail =
+ read_format_capacity(target->d_fd, &bsize);
+
+ /* sanity check. if not reasonable default to 4.7 GB */
+ if (nblks_avail < MAX_CD_BLKS) {
+ nblks_avail = MAX_DVD_BLKS;
+ }
+ }
+ }
+
+ if ((total_nblks + end_tno*300) > nblks_avail) {
+ err_msg(gettext("Not enough space on the media.\n"));
+ if (debug) {
+ (void) printf("Need %u only found %u \n",
+ (total_nblks + end_tno*300),
+ (uint32_t)nblks_avail);
+ }
+
+ exit(1);
+ }
+}
+
+/*
+ * This copies both audio and data CDs. It first reads the TOC of the source CD
+ * and creates a temp file with the CD image. After this is completed it creates
+ * the target CD using TAO mode.
+ */
+void
+copy_cd(void)
+{
+ cd_device *src;
+ char *p;
+ uchar_t *toc, end_tno;
+ int blksize, i;
+ int audio_cd, data_cd;
+ uint32_t total_nblks;
+ int ret;
+ struct t_data *tlist;
+
+ print_n_flush(gettext("Analyzing source CD..."));
+ (void) check_device(target,
+ CHECK_DEVICE_NOT_WRITABLE|EXIT_IF_CHECK_FAILED);
+
+ /* if source drive is specified on the command line */
+
+ if (copy_src) {
+ p = my_zalloc(PATH_MAX);
+ if (lookup_device(copy_src, p) == 0) {
+ err_msg(gettext("Cannot find device %s"), copy_src);
+ err_msg(gettext(" or no media in the drive\n"));
+ exit(1);
+ }
+ src = get_device(copy_src, p);
+ if (src == NULL) {
+ err_msg(gettext("Unable to open %s\n"), copy_src);
+ exit(1);
+ }
+ free(p);
+ } else {
+ /* source is same as target drive */
+ src = target;
+ }
+
+ (void) check_device(src, CHECK_TYPE_NOT_CDROM | CHECK_NO_MEDIA |
+ CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED);
+
+ toc = (uchar_t *)my_zalloc(4);
+ if (!read_toc(src->d_fd, 0, 0, 4, toc)) {
+ err_msg(gettext("Cannot read table of contents\n"));
+ exit(1);
+ }
+ end_tno = toc[3];
+ free(toc);
+ tlist = (struct t_data *)my_zalloc(end_tno * sizeof (struct t_data));
+
+ audio_cd = data_cd = 0;
+ total_nblks = 0;
+
+ /* build track information so we can copy it over */
+ for (i = 1; i <= end_tno; i++) {
+ struct track_info *ti;
+
+ ti = &tlist[i - 1].ti;
+ if (!build_track_info(src, i, ti)) {
+ err_msg(gettext(
+ "Cannot get information for track %d\n"), i);
+ exit(1);
+ }
+ total_nblks += ti->ti_track_size;
+ if (ti->ti_track_mode & 4)
+ data_cd = 1;
+ else
+ audio_cd = 1;
+
+ /* Now some sanity checks on the track information */
+ if ((ti->ti_flags & TI_SESSION_NO_VALID) &&
+ (ti->ti_session_no != 1)) {
+ err_msg(
+ gettext("Copying multisession CD is not supported\n"));
+ exit(1);
+ }
+ if ((ti->ti_flags & TI_PACKET_MODE) ||
+ (ti->ti_flags & TI_BLANK_TRACK) ||
+ (ti->ti_flags & TI_DAMAGED_TRACK) ||
+ (data_cd && audio_cd) || (ti->ti_data_mode == 2)) {
+
+ err_msg(gettext("CD format is not supported\n"));
+ exit(1);
+ }
+ if ((ti->ti_flags & TI_NWA_VALID) &&
+ (ti->ti_nwa != 0xffffffff)) {
+ err_msg(gettext("Cannot copy incomplete discs\n"));
+ exit(1);
+ }
+ }
+ /* l10n_NOTE : 'done' as in "Analyzing source CD...done" */
+ (void) printf(gettext("done.\n"));
+
+ if (data_cd) {
+ blksize = 2048;
+ } else {
+ /* audio cd */
+ blksize = 2352;
+ }
+
+ /* In case of audio CDs, build_track_info() returns 2352 sized nblks */
+ if (src->d_blksize == 512 && data_cd) {
+ total_nblks /= 4;
+ }
+ (void) printf(gettext("\nCopying %d %s track%s : %ld kbytes\n\n"),
+ end_tno, (audio_cd == 1) ? gettext("audio") : gettext("data"),
+ (end_tno > 1) ? "s" : "", (long)((total_nblks*blksize)/1024));
+
+ if (!check_avail_temp_space(total_nblks*blksize)) {
+ err_msg(gettext("Not enough space in temporary directory\n"));
+ err_msg(
+ gettext("Use -m to specify alternate temporary directory\n"));
+ exit(1);
+ }
+
+ /*
+ * If we can check available space on the target media at this
+ * Stage, then it is always better. We cannot check DVD+R(W)
+ * as this media may be formatted and not blank.
+ */
+ if (target && (src != target) && (device_type != DVD_PLUS) &&
+ (device_type != DVD_PLUS_W) && (!check_device(target,
+ CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK))) {
+ ensure_media_space(total_nblks, end_tno);
+ }
+
+ /* for each track */
+ for (i = 1; i <= end_tno; i++) {
+ tlist[i - 1].h = open_temp_file_stream();
+ if (tlist[i - 1].h == NULL) {
+ err_msg(gettext("Cannot create temporary file : %s\n"),
+ get_err_str());
+ exit(1);
+ }
+
+ if (audio_cd)
+ ret = read_audio_track(src, &tlist[i - 1].ti,
+ tlist[i - 1].h);
+ else
+ ret = read_data_track(src, &tlist[i - 1].ti,
+ tlist[i - 1].h);
+ if (ret == 0) {
+ err_msg(gettext("Error reading track %d : %s\n"), i,
+ get_err_str());
+ if (debug)
+ (void) printf("%x %x %x %x\n", uscsi_status,
+ SENSE_KEY(rqbuf), ASC(rqbuf), ASCQ(rqbuf));
+ exit(1);
+ }
+ }
+
+ /*
+ * We've finished copying the CD. If source and destination are the same
+ * or they where not specified then eject the disk and wait for a new
+ * disk to be inserted.
+ */
+
+ while ((target == NULL) ||
+ check_device(target, CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK)) {
+ if (target != NULL) {
+ (void) eject_media(target);
+ }
+
+ (void) printf("\n");
+ print_n_flush(
+ gettext("Insert a blank media in the drive and press Enter."));
+ (void) fflush(stdin);
+ if (target) {
+ fini_device(target);
+ target = NULL;
+ }
+ (void) getchar();
+ (void) sleep(4);
+ (void) setup_target(SCAN_WRITERS);
+ }
+ (void) printf("\n");
+ (void) setreuid(ruid, 0);
+
+ if ((device_type != DVD_PLUS) && (device_type != DVD_PLUS_W)) {
+ ensure_media_space(total_nblks, end_tno);
+ write_init(audio_cd ? TRACK_MODE_AUDIO : TRACK_MODE_DATA);
+ }
+
+ /* for each track */
+ for (i = 0; i < end_tno; i++) {
+ /*
+ * DVD's dont contain tracks and need to be written in DAO
+ * mode.
+ */
+ if (device_type != CD_RW) {
+ if (end_tno > 1) {
+ err_msg(gettext(
+ "Media state is not suitable for this"
+ " write mode.\n"));
+ }
+ write_mode = DAO_MODE;
+
+ /*
+ * DVD-R(W) and DVD+R needs to have space reserved
+ * prior to writing.
+ */
+ if ((device_type == DVD_MINUS) ||
+ (device_type == DVD_PLUS)) {
+ if (!set_reservation(target->d_fd,
+ total_nblks + 1)) {
+ (void) printf(gettext(
+ "Setting reservation failed\n"));
+ exit(1);
+ }
+ }
+ }
+
+ write_next_track(audio_cd ? TRACK_MODE_AUDIO : TRACK_MODE_DATA,
+ tlist[i].h);
+
+ /*
+ * Running in simulation mode and writing several tracks is
+ * useless so bail after the first track is done.
+ */
+
+ if (simulation && (end_tno != 1)) {
+ (void) printf(gettext(
+ "Simulation mode : skipping remaining tracks\n"));
+ break;
+ }
+ }
+
+ write_fini();
+ /* close the temp file handles */
+ for (i = 0; i < end_tno; i++)
+ (tlist[i].h)->bstr_close(tlist[i].h);
+ free(tlist);
+ fini_device(target);
+ exit(0);
+}