Mercurial > illumos > onarm
annotate usr/src/cmd/cdrw/copycd.c @ 4:1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Mon, 31 Aug 2009 14:38:03 +0900 |
parents | c9caec207d52 |
children |
rev | line source |
---|---|
0 | 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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
23 * Use is subject to license terms. | |
24 */ | |
25 | |
4
1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
Koji Uno <koji.uno@sun.com>
parents:
0
diff
changeset
|
26 #pragma ident "%Z%%M% %I% %E% SMI" |
0 | 27 |
28 #include <stdio.h> | |
29 #include <stdlib.h> | |
30 #include <sys/types.h> | |
31 #include <errno.h> | |
32 #include <limits.h> | |
33 #include <unistd.h> | |
34 #include <libintl.h> | |
35 #include <string.h> | |
36 | |
37 #include "main.h" | |
38 #include "util.h" | |
39 #include "misc_scsi.h" | |
40 #include "mmc.h" | |
41 #include "bstream.h" | |
42 #include "device.h" | |
43 #include "msgs.h" | |
44 #include "transport.h" | |
45 | |
46 struct t_data { | |
47 bstreamhandle h; | |
48 struct track_info ti; | |
49 }; | |
50 | |
51 int read_audio_track(cd_device *dev, struct track_info *ti, bstreamhandle h); | |
52 | |
53 #define READ_BURST 24 /* < 64K in all cases */ | |
54 | |
55 /* | |
56 * This reads the data off of a cd while updating the progress indicator. | |
57 * We want to do this in smaller chunks since some CD drives have | |
58 * problems with larger reads. | |
59 */ | |
60 static int | |
61 read_data_track(cd_device *dev, struct track_info *ti, bstreamhandle h) | |
62 { | |
63 int blksize; | |
64 uint32_t blks_read, cblk, read_chunk, read_size; | |
65 uchar_t *buf; | |
66 int ret, sav; | |
67 int link_blks_count; | |
68 | |
69 buf = NULL; | |
70 ret = 0; | |
71 | |
72 /* | |
73 * the last link_blks_count blocks may not exist or be completely | |
74 * filled. We need to record the amount to avoid bailing out if | |
75 * they cannot be read. | |
76 */ | |
77 | |
78 if (dev->d_blksize == 512) { | |
79 blksize = 512; | |
80 link_blks_count = 8; | |
81 } else { | |
82 blksize = 2048; | |
83 link_blks_count = 2; | |
84 } | |
85 | |
86 buf = (uchar_t *)my_zalloc(READ_BURST * blksize); | |
87 | |
88 print_n_flush(gettext("Reading track %d..."), ti->ti_track_no); | |
89 | |
90 if (verbose) | |
91 print_n_flush("Track size is %u...", ti->ti_track_size); | |
92 | |
93 init_progress(); | |
94 cblk = ti->ti_start_address; | |
95 blks_read = 0; | |
96 while (blks_read < ti->ti_track_size) { | |
97 /* Last few are special */ | |
98 read_chunk = ti->ti_track_size - blks_read - link_blks_count; | |
99 read_chunk = (read_chunk > READ_BURST) ? READ_BURST : | |
100 read_chunk; | |
101 if (read_chunk == 0) { | |
102 /* Time for last link blocks */ | |
103 read_chunk = link_blks_count; | |
104 } | |
105 read_size = read_chunk * blksize; | |
106 if (read10(dev->d_fd, cblk, read_chunk, buf, read_size)) { | |
107 if (h->bstr_write(h, buf, read_size) != read_size) { | |
108 goto read_data_track_failed; | |
109 } | |
110 } else { | |
111 if (blks_read != | |
112 (ti->ti_track_size - link_blks_count)) { | |
113 goto read_data_track_failed; | |
114 } else { | |
115 /* Read can fail for last link sectors */ | |
116 errno = 0; | |
117 } | |
118 } | |
119 blks_read += read_chunk; | |
120 cblk += read_chunk; | |
121 (void) progress((ti->ti_track_size), blks_read); | |
122 } | |
123 /* l10n_NOTE : 'done' as in "Reading track 1...done" */ | |
124 (void) str_print(gettext("done.\n"), progress_pos); | |
125 ret = 1; | |
126 read_data_track_failed: | |
127 sav = errno; | |
128 | |
129 free(buf); | |
130 errno = sav; | |
131 return (ret); | |
132 } | |
133 | |
134 static void | |
135 ensure_media_space(uint32_t total_nblks, uchar_t end_tno) | |
136 { | |
137 uint32_t nblks_avail; | |
138 uint_t bsize; | |
139 uint_t leadin_size = 0; | |
140 | |
141 get_media_type(target->d_fd); | |
142 | |
143 if (use_media_stated_capacity) { | |
144 nblks_avail = get_last_possible_lba(target); | |
145 | |
146 if (nblks_avail == 0) { | |
147 | |
148 /* most newer drives use READ FORMAT CAPACITY */ | |
149 nblks_avail = read_format_capacity(target->d_fd, | |
150 &bsize); | |
151 | |
152 /* if both methods fail no choice but to bail out */ | |
153 if (nblks_avail == 0) { | |
154 | |
155 err_msg(gettext( | |
156 "Cannot find out media capacity.\n")); | |
157 exit(1); | |
158 } | |
159 leadin_size = end_tno*300; | |
160 } | |
161 } else { | |
162 if (device_type == CD_RW) { | |
163 nblks_avail = MAX_CD_BLKS; | |
164 } else { | |
165 /* | |
166 * For DVD drives use read_format_capacity as default | |
167 * retrieve the media size, it can be 3.6, 3.9, 4.2, | |
168 * 4.7, or 9.2 GB | |
169 */ | |
170 nblks_avail = | |
171 read_format_capacity(target->d_fd, &bsize); | |
172 | |
173 /* sanity check. if not reasonable default to 4.7 GB */ | |
174 if (nblks_avail < MAX_CD_BLKS) { | |
175 nblks_avail = MAX_DVD_BLKS; | |
176 } | |
177 } | |
178 } | |
179 | |
180 if ((total_nblks + leadin_size) > nblks_avail) { | |
181 err_msg(gettext("Not enough space on the media.\n")); | |
182 if (debug) { | |
183 (void) printf("Need %u only found %u \n", | |
184 (total_nblks + leadin_size), | |
185 (uint32_t)nblks_avail); | |
186 } | |
187 | |
188 exit(1); | |
189 } | |
190 } | |
191 | |
192 /* | |
193 * This copies both audio and data CDs. It first reads the TOC of the source CD | |
194 * and creates a temp file with the CD image. After this is completed it creates | |
195 * the target CD using TAO mode. | |
196 */ | |
197 void | |
198 copy_cd(void) | |
199 { | |
200 cd_device *src; | |
201 char *p; | |
202 uchar_t *toc, end_tno; | |
203 int blksize, i; | |
204 int audio_cd, data_cd; | |
205 uint32_t total_nblks; | |
206 int ret; | |
207 struct t_data *tlist; | |
208 | |
209 print_n_flush(gettext("Analyzing source CD...")); | |
210 (void) check_device(target, | |
211 CHECK_DEVICE_NOT_WRITABLE|EXIT_IF_CHECK_FAILED); | |
212 | |
213 /* if source drive is specified on the command line */ | |
214 | |
215 if (copy_src) { | |
216 p = my_zalloc(PATH_MAX); | |
217 if (lookup_device(copy_src, p) == 0) { | |
218 err_msg(gettext("Cannot find device %s"), copy_src); | |
219 err_msg(gettext(" or no media in the drive\n")); | |
220 exit(1); | |
221 } | |
222 src = get_device(copy_src, p); | |
223 if (src == NULL) { | |
224 err_msg(gettext("Unable to open %s\n"), copy_src); | |
225 exit(1); | |
226 } | |
227 free(p); | |
228 } else { | |
229 /* source is same as target drive */ | |
230 src = target; | |
231 } | |
232 | |
233 (void) check_device(src, CHECK_TYPE_NOT_CDROM | CHECK_NO_MEDIA | | |
234 CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED); | |
235 | |
236 /* What type of media are we working with? */ | |
237 get_media_type(src->d_fd); | |
238 | |
239 toc = (uchar_t *)my_zalloc(4); | |
240 if (!read_toc(src->d_fd, 0, 0, 4, toc)) { | |
241 err_msg(gettext("Cannot read table of contents\n")); | |
242 exit(1); | |
243 } | |
244 end_tno = toc[3]; | |
245 free(toc); | |
246 tlist = (struct t_data *)my_zalloc(end_tno * sizeof (struct t_data)); | |
247 | |
248 audio_cd = data_cd = 0; | |
249 total_nblks = 0; | |
250 | |
251 /* build track information so we can copy it over */ | |
252 for (i = 1; i <= end_tno; i++) { | |
253 struct track_info *ti; | |
254 | |
255 ti = &tlist[i - 1].ti; | |
256 if (!build_track_info(src, i, ti)) { | |
257 err_msg(gettext( | |
258 "Cannot get information for track %d\n"), i); | |
259 exit(1); | |
260 } | |
261 total_nblks += ti->ti_track_size; | |
262 if (ti->ti_track_mode & 4) | |
263 data_cd = 1; | |
264 else | |
265 audio_cd = 1; | |
266 | |
267 /* Now some sanity checks on the track information */ | |
268 if ((ti->ti_flags & TI_SESSION_NO_VALID) && | |
269 (ti->ti_session_no != 1)) { | |
270 err_msg( | |
271 gettext("Copying multisession CD is not supported\n")); | |
272 exit(1); | |
273 } | |
274 if ((ti->ti_flags & TI_BLANK_TRACK) || | |
275 (ti->ti_flags & TI_DAMAGED_TRACK) || | |
276 (data_cd && audio_cd) || (ti->ti_data_mode == 2)) { | |
277 | |
278 err_msg(gettext("CD format is not supported\n")); | |
279 exit(1); | |
280 } | |
281 if ((ti->ti_flags & TI_NWA_VALID) && | |
282 (ti->ti_nwa != 0xffffffff)) { | |
283 err_msg(gettext("Cannot copy incomplete discs\n")); | |
284 exit(1); | |
285 } | |
286 } | |
287 /* l10n_NOTE : 'done' as in "Analyzing source CD...done" */ | |
288 (void) printf(gettext("done.\n")); | |
289 | |
290 if (data_cd) { | |
291 blksize = 2048; | |
292 } else { | |
293 /* audio cd */ | |
294 blksize = 2352; | |
295 } | |
296 | |
297 /* In case of audio CDs, build_track_info() returns 2352 sized nblks */ | |
298 if (src->d_blksize == 512 && data_cd) { | |
299 total_nblks /= 4; | |
300 } | |
301 (void) printf(gettext("\nCopying %d %s track%s : %ld kbytes\n\n"), | |
302 end_tno, (audio_cd == 1) ? gettext("audio") : gettext("data"), | |
303 (end_tno > 1) ? "s" : "", (long)((total_nblks*blksize)/1024)); | |
304 | |
305 if ((ret = check_avail_temp_space(total_nblks*blksize)) != 0) { | |
306 err_msg(gettext("Cannot use temporary directory : %s\n"), | |
307 strerror(ret)); | |
308 err_msg(gettext("Use -m to specify alternate" | |
309 " temporary directory\n")); | |
310 exit(1); | |
311 } | |
312 | |
313 /* | |
314 * If we can check available space on the target media at this | |
315 * Stage, then it is always better. We cannot check DVD+R(W) | |
316 * as this media may be formatted and not blank. | |
317 */ | |
318 if (target && (src != target) && (device_type != DVD_PLUS) && | |
319 (device_type != DVD_PLUS_W) && (!check_device(target, | |
320 CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK))) { | |
321 ensure_media_space(total_nblks, end_tno); | |
322 } | |
323 | |
324 /* for each track */ | |
325 for (i = 1; i <= end_tno; i++) { | |
326 tlist[i - 1].h = open_temp_file_stream(); | |
327 if (tlist[i - 1].h == NULL) { | |
328 err_msg(gettext("Cannot create temporary file : %s\n"), | |
329 get_err_str()); | |
330 exit(1); | |
331 } | |
332 | |
333 if (audio_cd) | |
334 ret = read_audio_track(src, &tlist[i - 1].ti, | |
335 tlist[i - 1].h); | |
336 else | |
337 ret = read_data_track(src, &tlist[i - 1].ti, | |
338 tlist[i - 1].h); | |
339 if (ret == 0) { | |
340 err_msg(gettext("Error reading track %d : %s\n"), i, | |
341 get_err_str()); | |
342 if (debug) | |
343 (void) printf("%x %x %x %x\n", uscsi_status, | |
344 SENSE_KEY(rqbuf), ASC(rqbuf), ASCQ(rqbuf)); | |
345 exit(1); | |
346 } | |
347 } | |
348 | |
349 /* | |
350 * We've finished copying the CD. If source and destination are the same | |
351 * or they where not specified then eject the disk and wait for a new | |
352 * disk to be inserted. | |
353 * | |
354 * Since, DVD+RWs are not blanked just reformated, allow the insertion | |
355 * of a DVD+RW to be the only condition necessary to complete copying. | |
356 */ | |
357 | |
358 do { | |
359 if (target != NULL) { | |
360 (void) eject_media(target); | |
361 } | |
362 | |
363 (void) printf("\n"); | |
364 print_n_flush( | |
365 gettext("Insert a blank media in the drive and press Enter.")); | |
366 (void) fflush(stdin); | |
367 if (target) { | |
368 fini_device(target); | |
369 target = NULL; | |
370 } | |
371 (void) getchar(); | |
372 (void) sleep(4); | |
373 (void) setup_target(SCAN_WRITERS); | |
374 if (target) | |
375 get_media_type(target->d_fd); | |
376 } while ((target == NULL) || | |
377 ((device_type == DVD_PLUS_W)? check_device(target, CHECK_NO_MEDIA): | |
378 check_device(target, CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK))); | |
379 | |
380 (void) printf("\n"); | |
381 (void) setreuid(ruid, 0); | |
382 | |
383 if ((device_type != DVD_PLUS) && (device_type != DVD_PLUS_W)) { | |
384 ensure_media_space(total_nblks, end_tno); | |
385 write_init(audio_cd ? TRACK_MODE_AUDIO : TRACK_MODE_DATA); | |
386 } | |
387 | |
388 /* | |
389 * Simulation writing can't happen on DVD+RW's | |
390 * or DVD+R's. According to the MMC spec this | |
391 * operation is not supported. So we should | |
392 * bail out if the user tries to do a simulation | |
393 * write. | |
394 */ | |
395 if (simulation && (device_type == DVD_PLUS_W || | |
396 device_type == DVD_PLUS)) { | |
397 err_msg(gettext("Media does not support simulated writing.\n")); | |
398 exit(1); | |
399 } | |
400 | |
401 if (device_type == DVD_PLUS_W) { | |
402 /* | |
403 * DVD+RW requires that we format the media before | |
404 * writing. | |
405 */ | |
406 (void) print_n_flush(gettext("Formatting media...")); | |
407 if (!format_media(target->d_fd)) { | |
408 (void) printf(gettext( | |
409 "Could not format media\n")); | |
410 exit(1); | |
411 } else { | |
412 int counter; | |
413 uchar_t *di; | |
414 | |
415 /* poll until format is done */ | |
416 di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE); | |
417 (void) sleep(10); | |
418 for (counter = 0; counter < 200; counter++) { | |
419 ret = read_disc_info(target->d_fd, di); | |
420 if ((SENSE_KEY(rqbuf) == 2) && | |
421 (ASC(rqbuf) == 4)) { | |
422 (void) print_n_flush("."); | |
423 (void) sleep(5); | |
424 } else { | |
425 break; | |
426 } | |
427 } | |
428 } | |
429 } | |
430 | |
431 /* for each track */ | |
432 for (i = 0; i < end_tno; i++) { | |
433 /* | |
434 * DVD's dont contain tracks and need to be written in DAO | |
435 * mode. | |
436 */ | |
437 if (device_type != CD_RW) { | |
438 if (end_tno > 1) { | |
439 err_msg(gettext( | |
440 "Media state is not suitable for this" | |
441 " write mode.\n")); | |
442 } | |
443 write_mode = DAO_MODE; | |
444 | |
445 /* | |
446 * DVD-R(W) and DVD+R needs to have space reserved | |
447 * prior to writing. | |
448 */ | |
449 if ((device_type == DVD_MINUS) || | |
450 (device_type == DVD_PLUS)) { | |
451 if (!set_reservation(target->d_fd, | |
452 total_nblks + 1)) { | |
453 (void) printf(gettext( | |
454 "Setting reservation failed\n")); | |
455 exit(1); | |
456 } | |
457 } | |
458 } | |
459 | |
460 write_next_track(audio_cd ? TRACK_MODE_AUDIO : TRACK_MODE_DATA, | |
461 tlist[i].h); | |
462 | |
463 /* | |
464 * Running in simulation mode and writing several tracks is | |
465 * useless so bail after the first track is done. | |
466 */ | |
467 | |
468 if (simulation && (end_tno != 1)) { | |
469 (void) printf(gettext( | |
470 "Simulation mode : skipping remaining tracks\n")); | |
471 break; | |
472 } | |
473 } | |
474 | |
475 write_fini(); | |
476 /* close the temp file handles */ | |
477 for (i = 0; i < end_tno; i++) | |
478 (tlist[i].h)->bstr_close(tlist[i].h); | |
479 free(tlist); | |
480 fini_device(target); | |
481 exit(0); | |
482 } |