2 Copyright (c) 2000-2012 Craig Condit, Stefan Huelswitt.
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
7 1. Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9 2. Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
13 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
14 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #define _LARGEFILE64_SOURCE
38 #include <sys/ioctl.h>
39 #include <netinet/in.h>
53 char *prg_name ="cdbackup";
54 char *cd_dev ="/dev/burner";
55 char *cdr_dev =0; /* no default here, too dangerous */
56 char *cd_label ="CDBackup Track";
58 long cd_len =-1; /* blocks */
59 int padsize =15; /* blocks */
70 char *exename ="cdrecord";
73 int cdrec_opt_count=0;
75 long long totalSize=0;
79 struct tm curtime; /* global, so multi-disks get all the same time */
82 /****************************************************************************/
84 char *make_arg(const char *format, ...)
89 if(vasprintf(&ptr,format,ap)<0) {
90 serror("No memory for cdrecord args\n");
97 void start_cdrecord(void)
102 if(!(p=args=calloc(cdrec_opt_count+32,sizeof(char *))))
103 serror("No memory for cdrecord args\n");
107 if(virt_dump || dvd) {
109 *p++=make_arg("tsize=%ds",secs);
110 /* work around the oddidity that cdrecord refuses to burn the disk, if
111 the tracksize is less than the first layer size on dual-layer disks */
112 if(layerbreak>0 && secs<layerbreak)
113 *p++=make_arg("driveropts=layerbreak=%ld",layerbreak);
118 *p++=make_arg("padsize=%ds",padsize);
121 *p++=make_arg("speed=%d",cd_speed);
122 *p++=make_arg("dev=%s",cdr_dev);
124 for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
126 if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
131 fprintf(stderr,"%s: cdrecord command:",prg_name);
132 for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
133 fprintf(stderr,"\n");
136 execvp(exename,args);
137 error("Exec failed (cdrecord)");
140 long atip_cdrecord(void)
146 if(asprintf(&cmd,"%s 2>&1 dev=%s -atip",exename,cdr_dev)<0) {
147 fprintf(stderr,"%s: error making atip command: %s\n",prg_name,strerror(errno));
150 DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
153 if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
156 while(fgets(buff,sizeof(buff),p)) {
158 /* get layerbreak position */
159 if(!strncmp(buff,"layer break at:",15)) layerbreak=strtol(&buff[15],NULL,10);
161 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
163 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
165 else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
170 if(size>0 && verbose) {
172 fprintf(stderr,"%s: auto-detected media size %s (%ld blocks, layerbreak at %ld)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size,layerbreak);
177 /****************************************************************************/
179 void parse_cmdline(char argc, char *argv[])
184 /* get some default from the environment */
185 val=getenv("CDR_DEVICE");
188 DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
190 val=getenv("CDR_SPEED");
192 cd_speed=strtol(val,NULL,10);
193 DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
196 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wRE:"))>0) {
198 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
199 "Copyright (C) 2000-2010\n"
200 "This is free software; see the source for copying conditions.\n"
201 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
202 "PARTICULAR PURPOSE.\n");
204 case 'v': verbose=1; break;
205 case 'm': multidisk=1; break;
206 case 'X': xamode2=1; break;
207 case 'c': multicmd=optarg; break;
208 case 'd': cd_dev=optarg; break;
209 case 'r': cdr_dev=optarg; break;
210 case 'a': cd_label=optarg; break;
211 case 'C': crc=0; break;
212 case 'i': virt_name=optarg; virtual=1; break;
213 case 'w': virt_dump=1; break;
214 case 'E': exename=optarg; break;
216 DEBUG("cdbackup: DVD mode enabled\n");
218 case 'D': verbose=1; debug=1;
219 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
221 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
223 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
224 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
226 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
227 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
229 default: fprintf(stderr,
230 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
231 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
232 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
233 " -l N set media size, disable auto-detect\n"
234 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
235 " -s N record CD at speed N (default 4)\n"
236 " -X enable CDROM XA2 mode in cdrecord\n"
237 " -a LABEL use LABEL as CD session title\n"
238 " -p N use a padsize of N sectors for the session (default 15)\n"
239 " -m enable multi-disk mode\n"
240 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
241 " -C disable checksum creation for datablocks\n"
242 " -i IMAGE use virtual image IMAGE for recording\n"
243 " -w dump virtual image to media\n"
244 " -R enables DVD mode\n"
245 " -E EXE set alternative cdrecord executable\n"
247 " -D enable DEBUG output\n"
248 " -V prints version & exits\n"
249 " -- pass rest of commandline to cdrecord\n"
255 if(optind<argc) { /* save position/count of cdrecord options */
256 cdrec_opt_count=argc-optind;
257 cdrec_opt=&argv[optind];
262 if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
264 if(virtual && dvd && !virt_dump) {
265 fprintf(stderr,"Option -R ignored in virtual mode\n");
269 if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
272 if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
273 if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
276 /****************************************************************************/
281 cd_len=atip_cdrecord();
282 if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
286 /****************************************************************************/
291 char buffer[CD_FRAMESIZE];
292 long long grandTotal;
299 Vopen(1); n=VreadToc(0);
300 if(n<1) serror("It's not usefull to dump an empty image");
301 secs=Vsize(); cont=VhasCont();
302 if(cd_len<secs) serror("Image doesn't fit to this media");
303 Vseek(-1); VprepareDump();
306 Vopen(0); n=VreadToc(0); VprintSpace();
308 fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
314 diskchange(multicmd,cd_dev);
319 fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
325 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
331 totalSize+=grandTotal;
333 char str1[16], str2[16];
334 fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
335 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
339 if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
344 diskchange(multicmd,cd_dev);
349 /****************************************************************************/
353 long long grandTotal=0;
354 struct header_block header;
355 int flags, datasize, result=0;
357 char buffer[CD_FRAMESIZE];
358 struct data_block *db=(struct data_block *)&buffer[0];
362 if(crc) { flags|=F_CRC; datasize-=4; }
364 sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
365 curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
367 strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
368 strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
369 strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
370 header.disk_set = disknum;
371 header.flags = flags;
374 fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
376 multidisk?"enabled":"disabled",
377 crc?"enabled":"disabled",
382 memset(buffer,0,CD_FRAMESIZE);
383 memcpy(buffer,&header,sizeof(struct header_block));
384 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
390 db->status=0; /* this isn't the last block (for now) */
391 bytes=full_read(0,&buffer[DBSIZE],datasize);
392 if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
393 db->datasize=htons(bytes);
395 if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */
396 if(db->status==0) { /* if not last block, mark disk as full */
402 int l=crc32(buffer,bytes+DBSIZE);
403 *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
405 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
406 } while(db->status==0);
408 if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
409 memset(buffer,0,CD_FRAMESIZE);
410 if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
411 while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
414 VcloseTrack(multidisk==0 ? 0 : result);
416 totalSize+=grandTotal;
418 char str1[16], str2[16];
419 fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
420 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
425 /****************************************************************************/
427 int main(int argc, char *argv[])
432 curtime_t=time(0); curtime=*localtime(&curtime_t);
433 parse_cmdline(argc,argv);
442 Vopen(0); result=VreadToc(0); VprintSpace();
445 if(disknum>1 && result!=0) {
447 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
448 diskchange(multicmd,cd_dev);
450 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
453 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
454 diskchange(multicmd,cd_dev);
456 else serror("Not enough free space on disk");
463 if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
466 if(!VisRegular()) diskchange(multicmd,cd_dev);