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;
78 struct tm curtime; /* global, so multi-disks get all the same time */
81 /****************************************************************************/
83 char *make_arg(const char *format, ...)
88 if(vasprintf(&ptr,format,ap)<0) {
89 serror("No memory for cdrecord args\n");
96 void start_cdrecord(void)
101 if(!(p=args=calloc(cdrec_opt_count+32,sizeof(char *))))
102 serror("No memory for cdrecord args\n");
106 if(virt_dump || dvd) {
108 *p++=make_arg("tsize=%ds",secs);
113 *p++=make_arg("padsize=%ds",padsize);
116 *p++=make_arg("speed=%d",cd_speed);
117 *p++=make_arg("dev=%s",cdr_dev);
119 for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
121 if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
126 fprintf(stderr,"%s: cdrecord command:",prg_name);
127 for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
128 fprintf(stderr,"\n");
131 execvp(exename,args);
132 error("Exec failed (cdrecord)");
135 long atip_cdrecord(void)
141 if(asprintf(&cmd,"%s 2>&1 dev=%s -atip",exename,cdr_dev)<0) {
142 fprintf(stderr,"%s: error making atip command: %s\n",prg_name,strerror(errno));
145 DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
148 if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
151 while(fgets(buff,sizeof(buff),p)) {
154 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
156 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
158 else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
163 if(size>0 && verbose) {
165 fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
170 /****************************************************************************/
172 void parse_cmdline(char argc, char *argv[])
177 /* get some default from the environment */
178 val=getenv("CDR_DEVICE");
181 DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
183 val=getenv("CDR_SPEED");
185 cd_speed=strtol(val,NULL,10);
186 DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
189 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wRE:"))>0) {
191 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
192 "Copyright (C) 2000-2010\n"
193 "This is free software; see the source for copying conditions.\n"
194 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
195 "PARTICULAR PURPOSE.\n");
197 case 'v': verbose=1; break;
198 case 'm': multidisk=1; break;
199 case 'X': xamode2=1; break;
200 case 'c': multicmd=optarg; break;
201 case 'd': cd_dev=optarg; break;
202 case 'r': cdr_dev=optarg; break;
203 case 'a': cd_label=optarg; break;
204 case 'C': crc=0; break;
205 case 'i': virt_name=optarg; virtual=1; break;
206 case 'w': virt_dump=1; break;
207 case 'E': exename=optarg; break;
209 DEBUG("cdbackup: DVD mode enabled\n");
211 case 'D': verbose=1; debug=1;
212 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
214 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
216 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
217 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
219 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
220 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
222 default: fprintf(stderr,
223 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
224 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
225 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
226 " -l N set media size, disable auto-detect\n"
227 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
228 " -s N record CD at speed N (default 4)\n"
229 " -X enable CDROM XA2 mode in cdrecord\n"
230 " -a LABEL use LABEL as CD session title\n"
231 " -p N use a padsize of N sectors for the session (default 15)\n"
232 " -m enable multi-disk mode\n"
233 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
234 " -C disable checksum creation for datablocks\n"
235 " -i IMAGE use virtual image IMAGE for recording\n"
236 " -w dump virtual image to media\n"
237 " -R enables DVD mode\n"
238 " -E EXE set alternative cdrecord executable\n"
240 " -D enable DEBUG output\n"
241 " -V prints version & exits\n"
242 " -- pass rest of commandline to cdrecord\n"
248 if(optind<argc) { /* save position/count of cdrecord options */
249 cdrec_opt_count=argc-optind;
250 cdrec_opt=&argv[optind];
255 if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
257 if(virtual && dvd && !virt_dump) {
258 fprintf(stderr,"Option -R ignored in virtual mode\n");
262 if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
265 if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
266 if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
269 /****************************************************************************/
274 cd_len=atip_cdrecord();
275 if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
279 /****************************************************************************/
284 char buffer[CD_FRAMESIZE];
285 long long grandTotal;
292 Vopen(1); n=VreadToc(0);
293 if(n<1) serror("It's not usefull to dump an empty image");
294 secs=Vsize(); cont=VhasCont();
295 if(cd_len<secs) serror("Image doesn't fit to this media");
296 Vseek(-1); VprepareDump();
299 Vopen(0); n=VreadToc(0); VprintSpace();
301 fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
307 diskchange(multicmd,cd_dev);
312 fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
318 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
324 totalSize+=grandTotal;
326 char str1[16], str2[16];
327 fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
328 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
332 if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
337 diskchange(multicmd,cd_dev);
342 /****************************************************************************/
346 long long grandTotal=0;
347 struct header_block header;
348 int flags, datasize, result=0;
350 char buffer[CD_FRAMESIZE];
351 struct data_block *db=(struct data_block *)&buffer[0];
355 if(crc) { flags|=F_CRC; datasize-=4; }
357 sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
358 curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
360 strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
361 strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
362 strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
363 header.disk_set = disknum;
364 header.flags = flags;
367 fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
369 multidisk?"enabled":"disabled",
370 crc?"enabled":"disabled",
375 memset(buffer,0,CD_FRAMESIZE);
376 memcpy(buffer,&header,sizeof(struct header_block));
377 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
383 db->status=0; /* this isn't the last block (for now) */
384 bytes=full_read(0,&buffer[DBSIZE],datasize);
385 if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
386 db->datasize=htons(bytes);
388 if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */
389 if(db->status==0) { /* if not last block, mark disk as full */
395 int l=crc32(buffer,bytes+DBSIZE);
396 *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
398 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
399 } while(db->status==0);
401 if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
402 memset(buffer,0,CD_FRAMESIZE);
403 if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
404 while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
407 VcloseTrack(multidisk==0 ? 0 : result);
409 totalSize+=grandTotal;
411 char str1[16], str2[16];
412 fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
413 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
418 /****************************************************************************/
420 int main(int argc, char *argv[])
425 curtime_t=time(0); curtime=*localtime(&curtime_t);
426 parse_cmdline(argc,argv);
435 Vopen(0); result=VreadToc(0); VprintSpace();
438 if(disknum>1 && result!=0) {
440 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
441 diskchange(multicmd,cd_dev);
443 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
446 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
447 diskchange(multicmd,cd_dev);
449 else serror("Not enough free space on disk");
456 if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
459 if(!VisRegular()) diskchange(multicmd,cd_dev);