#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <libintl.h>
#include <string.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
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;
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) {
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) {
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 {
errno = 0;
}
}
blks_read += read_chunk;
cblk += read_chunk;
(void) progress((ti->ti_track_size), blks_read);
}
(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)
{
uint32_t nblks_avail;
uint_t bsize;
uint_t leadin_size = 0;
get_media_type(target->d_fd);
if (device_type == CD_RW) {
nblks_avail = get_last_possible_lba(target);
if (nblks_avail == 0) {
nblks_avail = read_format_capacity(target->d_fd,
&bsize);
if (nblks_avail == 0) {
err_msg(gettext("Unable to determine media "
"capacity. Defaulting to 650 MB (74 minute)"
" disc.\n"));
nblks_avail = MAX_CD_BLKS;
}
leadin_size = end_tno*300;
}
} else {
nblks_avail = read_format_capacity(target->d_fd, &bsize);
if (nblks_avail < MAX_CD_BLKS) {
nblks_avail = MAX_DVD_BLKS;
}
}
if ((total_nblks + leadin_size) > nblks_avail) {
err_msg(gettext("Not enough space on the media.\n"));
if (debug) {
(void) printf("Need %u only found %u \n",
(total_nblks + leadin_size),
(uint32_t)nblks_avail);
}
exit(1);
}
}
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 (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 {
src = target;
}
(void) check_device(src, CHECK_TYPE_NOT_CDROM | CHECK_NO_MEDIA |
CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED);
get_media_type(src->d_fd);
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;
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;
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_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);
}
}
(void) printf(gettext("done.\n"));
if (data_cd) {
blksize = 2048;
} else {
blksize = 2352;
}
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 ((ret = check_avail_temp_space(total_nblks*blksize)) != 0) {
err_msg(gettext("Cannot use temporary directory : %s\n"),
strerror(ret));
err_msg(gettext("Use -m to specify alternate"
" temporary directory\n"));
exit(1);
}
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 (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);
}
}
do {
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);
if (target)
get_media_type(target->d_fd);
} while ((target == NULL) ||
((device_type == DVD_PLUS_W)? check_device(target, CHECK_NO_MEDIA):
check_device(target, CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK)));
(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);
}
if (simulation && (device_type == DVD_PLUS_W ||
device_type == DVD_PLUS)) {
err_msg(gettext("Media does not support simulated writing.\n"));
exit(1);
}
if (device_type == DVD_PLUS_W) {
(void) print_n_flush(gettext("Formatting media..."));
if (!format_media(target->d_fd)) {
(void) printf(gettext(
"Could not format media\n"));
exit(1);
} else {
int counter;
uchar_t *di;
di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
(void) sleep(10);
for (counter = 0; counter < 200; counter++) {
ret = read_disc_info(target->d_fd, di);
if ((SENSE_KEY(rqbuf) == 2) &&
(ASC(rqbuf) == 4)) {
(void) print_n_flush(".");
(void) sleep(5);
} else {
break;
}
}
}
}
for (i = 0; i < end_tno; i++) {
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;
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);
if (simulation && (end_tno != 1)) {
(void) printf(gettext(
"Simulation mode : skipping remaining tracks\n"));
break;
}
}
write_fini();
for (i = 0; i < end_tno; i++)
(tlist[i].h)->bstr_close(tlist[i].h);
free(tlist);
fini_device(target);
exit(0);
}