2 Copyright (c) 2000-2010 Craig Condit, Stefan Hülswitt.
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 asprintf(&cmd,"%s 2>&1 dev=%s -atip",exename,cdr_dev);
142 DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
145 if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
148 while(fgets(buff,sizeof(buff),p)) {
151 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
153 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
155 else if(!strncmp(buff," ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
160 if(size>0 && verbose) {
162 fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
167 /****************************************************************************/
169 void parse_cmdline(char argc, char *argv[])
174 /* get some default from the environment */
175 val=getenv("CDR_DEVICE");
178 DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
180 val=getenv("CDR_SPEED");
182 cd_speed=strtol(val,NULL,10);
183 DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
186 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wRE:"))>0) {
188 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
189 "Copyright (C) 2000-2010\n"
190 "This is free software; see the source for copying conditions.\n"
191 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
192 "PARTICULAR PURPOSE.\n");
194 case 'v': verbose=1; break;
195 case 'm': multidisk=1; break;
196 case 'X': xamode2=1; break;
197 case 'c': multicmd=optarg; break;
198 case 'd': cd_dev=optarg; break;
199 case 'r': cdr_dev=optarg; break;
200 case 'a': cd_label=optarg; break;
201 case 'C': crc=0; break;
202 case 'i': virt_name=optarg; virtual=1; break;
203 case 'w': virt_dump=1; break;
204 case 'E': exename=optarg; break;
206 DEBUG("cdbackup: DVD mode enabled\n");
208 case 'D': verbose=1; debug=1;
209 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
211 case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
213 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
214 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
216 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
217 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
219 default: fprintf(stderr,
220 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
221 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
222 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
223 " -l N set media size, disable auto-detect\n"
224 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
225 " -s N record CD at speed N (default 4)\n"
226 " -X enable CDROM XA2 mode in cdrecord\n"
227 " -a LABEL use LABEL as CD session title\n"
228 " -p N use a padsize of N sectors for the session (default 15)\n"
229 " -m enable multi-disk mode\n"
230 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
231 " -C disable checksum creation for datablocks\n"
232 " -i IMAGE use virtual image IMAGE for recording\n"
233 " -w dump virtual image to media\n"
234 " -R enables DVD mode\n"
235 " -E EXE set alternative cdrecord executable\n"
237 " -D enable DEBUG output\n"
238 " -V prints version & exits\n"
239 " -- pass rest of commandline to cdrecord\n"
245 if(optind<argc) { /* save position/count of cdrecord options */
246 cdrec_opt_count=argc-optind;
247 cdrec_opt=&argv[optind];
252 if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
254 if(virtual && dvd && !virt_dump) {
255 fprintf(stderr,"Option -R ignored in virtual mode\n");
259 if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
262 if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
263 if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
266 /****************************************************************************/
271 cd_len=atip_cdrecord();
272 if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
276 /****************************************************************************/
281 char buffer[CD_FRAMESIZE];
282 long long grandTotal;
289 Vopen(1); n=VreadToc(0);
290 if(n<1) serror("It's not usefull to dump an empty image");
291 secs=Vsize(); cont=VhasCont();
292 if(cd_len<secs) serror("Image doesn't fit to this media");
293 Vseek(-1); VprepareDump();
296 Vopen(0); n=VreadToc(0); VprintSpace();
298 fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
304 diskchange(multicmd,cd_dev);
309 fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
315 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
321 totalSize+=grandTotal;
323 char str1[16], str2[16];
324 fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
325 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
329 if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
334 diskchange(multicmd,cd_dev);
339 /****************************************************************************/
343 long long grandTotal=0;
344 struct header_block header;
345 int flags, datasize, result=0;
347 char buffer[CD_FRAMESIZE];
348 struct data_block *db=(struct data_block *)&buffer[0];
352 if(crc) { flags|=F_CRC; datasize-=4; }
354 sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
355 curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
357 strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
358 strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
359 strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
360 header.disk_set = disknum;
361 header.flags = flags;
364 fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
366 multidisk?"enabled":"disabled",
367 crc?"enabled":"disabled",
372 memset(buffer,0,CD_FRAMESIZE);
373 memcpy(buffer,&header,sizeof(struct header_block));
374 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
380 db->status=0; /* this isn't the last block (for now) */
381 bytes=full_read(0,&buffer[DBSIZE],datasize);
382 if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
383 db->datasize=htons(bytes);
385 if(cd_avail<(CD_FRAMESIZE*2)) { /* less than 2 block free */
386 if(db->status==0) { /* if not last block, mark disk as full */
392 int l=crc32(buffer,bytes+DBSIZE);
393 *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
395 Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
396 } while(db->status==0);
398 if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
399 memset(buffer,0,CD_FRAMESIZE);
400 if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
401 while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
406 totalSize+=grandTotal;
408 char str1[16], str2[16];
409 fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
410 prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
415 /****************************************************************************/
417 int main(int argc, char *argv[])
422 curtime_t=time(0); curtime=*localtime(&curtime_t);
423 parse_cmdline(argc,argv);
432 Vopen(0); result=VreadToc(0); VprintSpace();
435 if(disknum>1 && result!=0) {
437 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
438 diskchange(multicmd,cd_dev);
440 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
443 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
444 diskchange(multicmd,cd_dev);
446 else serror("Not enough free space on disk");
453 if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
456 if(!VisRegular()) diskchange(multicmd,cd_dev);