diff options
Diffstat (limited to 'usr/src/cmd/cdrw/copycd.c')
| -rw-r--r-- | usr/src/cmd/cdrw/copycd.c | 427 |
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); +} |
