nrelease - fix/improve livecd
[dragonfly.git] / usr.sbin / burncd / burncd.c
CommitLineData
9d0b6240 1/*-
f6e8a0a1 2 * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
9d0b6240
SW
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: src/usr.sbin/burncd/burncd.c,v 1.10.2.6 2002/11/20 00:26:18 njl Exp $
9d0b6240
SW
29 */
30
31#include <unistd.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <err.h>
36#include <sysexits.h>
37#include <fcntl.h>
38#include <sys/errno.h>
39#include <sys/ioctl.h>
40#include <sys/stat.h>
41#include <sys/cdio.h>
42#include <sys/cdrio.h>
43#include <sys/param.h>
44
45#define BLOCKS 16
46
47struct track_info {
48 int file;
49 char *file_name;
50 u_int file_size;
51 int block_size;
52 int block_type;
53 int pregap;
54 int addr;
55};
56static struct track_info tracks[100];
57static int fd, quiet, verbose, saved_block_size, notracks;
58
25809140
SW
59static void add_track(char *, int, int, int);
60static void do_DAO(int, int);
61static void do_TAO(int, int);
62static int write_file(struct track_info *);
63static int roundup_blocks(struct track_info *);
64static void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
65static void cleanup(int);
fe08e20d 66static void usage(void) __dead2;
9d0b6240
SW
67
68int
69main(int argc, char **argv)
70{
71 int ch, arg, addr;
72 int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
73 int nogap = 0, speed = 4 * 177, test_write = 0;
74 int block_size = 0, block_type = 0, cdopen = 0;
75 const char *dev = "/dev/acd0c";
76
77 while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
78 switch (ch) {
79 case 'd':
80 dao = 1;
81 break;
82
83 case 'e':
84 eject = 1;
85 break;
86
87 case 'f':
88 dev = optarg;
89 break;
90
91 case 'l':
92 list = 1;
93 break;
94
95 case 'm':
96 multi = 1;
97 break;
98
99 case 'n':
100 nogap = 1;
101 break;
102
103 case 'p':
104 preemp = 1;
105 break;
106
107 case 'q':
108 quiet = 1;
109 break;
110
111 case 's':
112 if (strcasecmp("max", optarg) == 0)
113 speed = CDR_MAX_SPEED;
114 else
115 speed = atoi(optarg) * 177;
116 if (speed <= 0)
117 errx(EX_USAGE, "Invalid speed: %s", optarg);
118 break;
119
120 case 't':
121 test_write = 1;
122 break;
123
124 case 'v':
125 verbose = 1;
126 break;
127
128 default:
129 usage();
130 }
131 }
132 argc -= optind;
133 argv += optind;
134
135 if (argc == 0)
136 usage();
137
138 if ((fd = open(dev, O_RDWR, 0)) < 0)
139 err(EX_NOINPUT, "open(%s)", dev);
140
141 if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
142 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
143
144 if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
145 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
146
147 err_set_exit(cleanup);
148
149 for (arg = 0; arg < argc; arg++) {
150 if (!strcasecmp(argv[arg], "fixate")) {
151 fixate = 1;
152 break;
153 }
154 if (!strcasecmp(argv[arg], "msinfo")) {
155 struct ioc_read_toc_single_entry entry;
156 struct ioc_toc_header header;
157
158 if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
159 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
160 bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
161 entry.address_format = CD_LBA_FORMAT;
162 entry.track = header.ending_track;
163 if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
164 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
165 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
166 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
167 fprintf(stdout, "%d,%d\n",
168 ntohl(entry.entry.addr.lba), addr);
169
170 break;
171 }
172 if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){
173 int error, blank, percent;
174
175 if (!strcasecmp(argv[arg], "erase"))
176 blank = CDR_B_ALL;
177 else
178 blank = CDR_B_MIN;
179 if (!quiet)
180 fprintf(stderr, "%sing CD, please wait..\r",
181 blank == CDR_B_ALL ? "eras" : "blank");
182
183 if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
184 err(EX_IOERR, "ioctl(CDRIOCBLANK)");
185 while (1) {
186 sleep(1);
187 error = ioctl(fd, CDRIOCGETPROGRESS, &percent);
188 if (percent > 0 && !quiet)
189 fprintf(stderr,
190 "%sing CD - %d %% done \r",
191 blank == CDR_B_ALL ?
192 "eras" : "blank", percent);
193 if (error || percent == 100)
194 break;
195 }
196 if (!quiet)
197 printf("\n");
198 continue;
199 }
200 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
201 block_type = CDR_DB_RAW;
202 block_size = 2352;
203 continue;
204 }
205 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
206 block_type = CDR_DB_ROM_MODE1;
207 block_size = 2048;
208 continue;
209 }
210 if (!strcasecmp(argv[arg], "mode2")) {
211 block_type = CDR_DB_ROM_MODE2;
212 block_size = 2336;
213 continue;
214 }
215 if (!strcasecmp(argv[arg], "xamode1")) {
216 block_type = CDR_DB_XA_MODE1;
217 block_size = 2048;
218 continue;
219 }
220 if (!strcasecmp(argv[arg], "xamode2")) {
221 block_type = CDR_DB_XA_MODE2_F2;
222 block_size = 2324;
223 continue;
224 }
225 if (!strcasecmp(argv[arg], "vcd")) {
226 block_type = CDR_DB_XA_MODE2_F2;
227 block_size = 2352;
228 dao = 1;
229 nogap = 1;
230 continue;
231 }
232 if (!block_size)
233 errx(EX_NOINPUT, "no data format selected");
234 if (list) {
235 char file_buf[MAXPATHLEN + 1], *eol;
236 FILE *fp;
237
238 if ((fp = fopen(argv[arg], "r")) == NULL)
239 err(EX_NOINPUT, "fopen(%s)", argv[arg]);
240
241 while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
242 if (*file_buf == '#' || *file_buf == '\n')
243 continue;
244 if ((eol = strchr(file_buf, '\n')))
245 *eol = 0;
246 add_track(file_buf, block_size, block_type, nogap);
247 }
248 if (feof(fp))
249 fclose(fp);
250 else
251 err(EX_IOERR, "fgets(%s)", file_buf);
252 }
253 else
254 add_track(argv[arg], block_size, block_type, nogap);
255 }
256 if (notracks) {
257 if (ioctl(fd, CDIOCSTART, 0) < 0)
258 err(EX_IOERR, "ioctl(CDIOCSTART)");
259 if (!cdopen) {
260 if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
261 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
262 cdopen = 1;
263 }
264 if (dao)
265 do_DAO(test_write, multi);
266 else
267 do_TAO(test_write, preemp);
268 }
269 if (fixate && !dao) {
270 if (!quiet)
271 fprintf(stderr, "fixating CD, please wait..\n");
272 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
273 err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
274 }
275
276 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
277 err_set_exit(NULL);
278 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
279 }
280
281 if (eject)
282 if (ioctl(fd, CDIOCEJECT) < 0)
283 err(EX_IOERR, "ioctl(CDIOCEJECT)");
284 close(fd);
285 exit(EX_OK);
286}
287
25809140 288static void
9d0b6240
SW
289add_track(char *name, int block_size, int block_type, int nogap)
290{
291 struct stat sb;
292 int file;
293 static int done_stdin = 0;
294
295 if (!strcmp(name, "-")) {
296 if (done_stdin) {
297 warn("skipping multiple usages of stdin");
298 return;
299 }
300 file = STDIN_FILENO;
301 done_stdin = 1;
302 }
303 else if ((file = open(name, O_RDONLY, 0)) < 0)
304 err(EX_NOINPUT, "open(%s)", name);
305 if (fstat(file, &sb) < 0)
306 err(EX_IOERR, "fstat(%s)", name);
307 tracks[notracks].file = file;
308 tracks[notracks].file_name = name;
309 if (file == STDIN_FILENO)
310 tracks[notracks].file_size = -1;
311 else
312 tracks[notracks].file_size = sb.st_size;
313 tracks[notracks].block_size = block_size;
314 tracks[notracks].block_type = block_type;
315
316 if (nogap && notracks)
317 tracks[notracks].pregap = 0;
318 else {
319 if (tracks[notracks - (notracks > 0)].block_type == block_type)
320 tracks[notracks].pregap = 150;
321 else
322 tracks[notracks].pregap = 255;
323 }
324
325 if (verbose) {
326 int pad = 0;
327
328 if (tracks[notracks].file_size / tracks[notracks].block_size !=
329 roundup_blocks(&tracks[notracks]))
330 pad = 1;
331 fprintf(stderr,
332 "adding type 0x%02x file %s size %d KB %d blocks %s\n",
333 tracks[notracks].block_type, name, (int)sb.st_size/1024,
334 roundup_blocks(&tracks[notracks]),
335 pad ? "(0 padded)" : "");
336 }
337 notracks++;
338}
339
25809140 340static void
9d0b6240
SW
341do_DAO(int test_write, int multi)
342{
343 struct cdr_cuesheet sheet;
344 struct cdr_cue_entry cue[100];
345 int format = CDR_SESS_CDROM;
346 int addr, i, j = 0;
347
348 int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
349 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
350
351 int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
352 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
353
354 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
355 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
356 if (verbose)
357 fprintf(stderr, "next writeable LBA %d\n", addr);
358
359 cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
360 (bt2df[tracks[0].block_type] & 0xf0) |
361 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
362
363 for (i = 0; i < notracks; i++) {
364 if (bt2ctl[tracks[i].block_type] < 0 ||
365 bt2df[tracks[i].block_type] < 0)
366 err(EX_IOERR, "track type not supported in DAO mode");
367
368 if (tracks[i].block_type >= CDR_DB_XA_MODE1)
369 format = CDR_SESS_CDROM_XA;
370
371 if (i == 0) {
372 addr += tracks[i].pregap;
373 tracks[i].addr = addr;
374
375 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
376 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
377 0x00, addr);
378
379 }
380 else {
381 if (tracks[i].pregap) {
382 if (tracks[i].block_type > 0x7) {
383 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
384 0x01, i+1, 0x0,
385 (bt2df[tracks[i].block_type] & 0xf0) |
386 (tracks[i].block_type < 8 ? 0x01 :0x04),
387 0x00, addr);
388 }
389 else
390 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
391 0x01, i+1, 0x0,
392 bt2df[tracks[i].block_type],
393 0x00, addr);
394 }
395 tracks[i].addr = tracks[i - 1].addr +
396 roundup_blocks(&tracks[i - 1]);
397
398 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
399 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
400 0x00, addr + tracks[i].pregap);
401
402 if (tracks[i].block_type > 0x7)
403 addr += tracks[i].pregap;
404 }
405 addr += roundup_blocks(&tracks[i]);
406 }
407
408 cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
409 (bt2df[tracks[i - 1].block_type] & 0xf0) |
410 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
411
412 sheet.len = j * 8;
413 sheet.entries = cue;
414 sheet.test_write = test_write;
415 sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
416 sheet.session_format = format;
417 if (verbose) {
418 u_int8_t *ptr = (u_int8_t *)sheet.entries;
419
420 fprintf(stderr,"CUE sheet:");
421 for (i = 0; i < sheet.len; i++)
422 if (i % 8)
423 fprintf(stderr," %02x", ptr[i]);
424 else
425 fprintf(stderr,"\n%02x", ptr[i]);
426 fprintf(stderr,"\n");
427 }
428
429 if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
430 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
431
432 for (i = 0; i < notracks; i++) {
433 if (write_file(&tracks[i]))
434 err(EX_IOERR, "write_file");
435 }
436
437 ioctl(fd, CDRIOCFLUSH);
438}
439
25809140 440static void
9d0b6240
SW
441do_TAO(int test_write, int preemp)
442{
443 struct cdr_track track;
444 int i;
445
446 for (i = 0; i < notracks; i++) {
447 track.test_write = test_write;
448 track.datablock_type = tracks[i].block_type;
449 track.preemp = preemp;
450 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
451 err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
452
453 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
454 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
455 if (!quiet)
456 fprintf(stderr, "next writeable LBA %d\n",
457 tracks[i].addr);
458 if (write_file(&tracks[i]))
459 err(EX_IOERR, "write_file");
460 if (ioctl(fd, CDRIOCFLUSH) < 0)
461 err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
462 }
463}
464
25809140 465static int
9d0b6240
SW
466write_file(struct track_info *track_info)
467{
468 int size, count, filesize;
469 char buf[2352*BLOCKS];
470 static int tot_size = 0;
471
472 filesize = track_info->file_size / 1024;
473
474 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
475 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
476
477 if (track_info->addr >= 0)
478 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
479
480 if (verbose)
481 fprintf(stderr, "addr = %d size = %d blocks = %d\n",
482 track_info->addr, track_info->file_size,
483 roundup_blocks(track_info));
484
485 if (!quiet) {
486 if (track_info->file == STDIN_FILENO)
487 fprintf(stderr, "writing from stdin\n");
488 else
489 fprintf(stderr,
490 "writing from file %s size %d KB\n",
491 track_info->file_name, filesize);
492 }
493 size = 0;
494
495 while ((count = read(track_info->file, buf,
496 MIN((track_info->file_size - size),
497 track_info->block_size * BLOCKS))) > 0) {
498 int res;
499
500 if (count % track_info->block_size) {
501 /* pad file to % block_size */
502 bzero(&buf[count],
503 (track_info->block_size * BLOCKS) - count);
504 count = ((count / track_info->block_size) + 1) *
505 track_info->block_size;
506 }
507 if ((res = write(fd, buf, count)) != count) {
508 fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n",
509 res, count, errno);
510 break;
511 }
512 size += count;
513 tot_size += count;
514 if (!quiet) {
515 int pct;
516
517 fprintf(stderr, "written this track %d KB", size/1024);
518 if (track_info->file != STDIN_FILENO && filesize) {
519 pct = (size / 1024) * 100 / filesize;
520 fprintf(stderr, " (%d%%)", pct);
521 }
522 fprintf(stderr, " total %d KB\r", tot_size/1024);
523 }
524 if (size >= track_info->file_size)
525 break;
526 }
527
528 if (!quiet)
529 fprintf(stderr, "\n");
530 close(track_info->file);
531 return 0;
532}
533
25809140 534static int
9d0b6240
SW
535roundup_blocks(struct track_info *track)
536{
537 return ((track->file_size + track->block_size - 1) / track->block_size);
538}
539
25809140 540static void
9d0b6240
SW
541cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
542 int dataform, int scms, int lba)
543{
544 cue->adr = adr;
545 cue->ctl = ctl;
546 cue->track = track;
547 cue->index = idx;
548 cue->dataform = dataform;
549 cue->scms = scms;
550 lba += 150;
551 cue->min = lba / (60*75);
552 cue->sec = (lba % (60*75)) / 75;
553 cue->frame = (lba % (60*75)) % 75;
554}
555
25809140 556static void
9d0b6240
SW
557cleanup(int dummy __unused)
558{
559 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0)
560 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
561}
562
25809140 563static void
9d0b6240
SW
564usage(void)
565{
566 fprintf(stderr,
567 "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
568 " [command file ...]\n", getprogname());
569 exit(EX_USAGE);
570}